Skip to content
Snippets Groups Projects
user avatar
Robert Fentress authored
0c8ef877
Forked from Robert A Fentress / torgersen
Source project has a limited visibility.

Torgersen Wordpress Theme

About

The Torgersen Wordpress Theme is a WordPress theme designed from the ground up to be maximally accessible to people with many different abilities, such as blind/low vision individuals and those with motor or cognitive disabilities. It is made available for use and modification by units at Virginia Tech. It can be downloaded and used as is (must add compiled version as zip eventually) or altered to suit the needs of your particular organization, if you have the technical skills to do so.

Technologies Used

The Torgersen Wordpress Theme uses version 8.5.0 of the Sage starter theme as its base, and is modeled after the Moss template used by Virginia Tech's Ensemble content management system.

Sage uses Gulp as its build system and Bower to manage front-end packages. It uses the official Sass port of Twitter Bootstrap framework as the foundation for its responsive grid-based layout and most of its interactive widgets.

Assumptions

To make changes to this theme, you should have a basic understanding of WordPress theme development generally and the Sage starter theme specifically, the Bootstrap responsive design framework, the CSS extension language, SASS, the NodeJS and Bower package management systems, and the Gulp automation toolkit. Tutorials on many of these topics are available from the Viginia Tech-licensed Lynda training library.

These instructions also assume you have a development instance of Wordpress running with our sample test data loaded into it. Assistive Technologies makes available the Assist Docker Wordpress development environment based on the Visible Wordpress Starter and this comes pre-populated with the test data.

Prerequisites

* Install Phantom JS as per instructions at PhantomJS and Mac OS X . Installing from brew recommended This may no longer be necessary. Must test and update instructions.

  1. Install node.js. We recommend you update to the latest version of npm: npm install -g npm@latest.
  2. Install gulp and Bower globally with npm install -g gulp bower

Configuration

In the Torgersen theme you've just checked out, locate the file assets/manifest.json and make the following changes to it:

Under paths, change the value of root to reflect the full path to the root of the project on your local development environment.

...
  "paths": {
    "root": "/path/to/torgersen/",
...
  }

Note: In the code examples, the elipses, ..., represents code that has been ommitted from the example.

Update devUrl to reflect your local development host.

For example, if your local development URL is http://192.168.99.100:8080 you would update the relevant section of the file to read:

...
  "config": {
    "devUrl": "http://192.168.99.100:8080"
  }
...

If your local development URL looks like http://localhost:8888/project-name/ you would update the file to read:

...
  "config": {
    "devUrl": "http://localhost:8888/project-name/"
  }
...

Install Node and Bower Packages

From the project root run:

  1. npm install
  2. bower install

You now have all the necessary dependencies to run the build process.

Gulp Build and Watch Commands

Note: Until you compile your assets with one of these commands, your site will appear broken when loaded in the browser

  • gulp — Compile and optimize the files in your assets directory
  • gulp watch — Compile assets when file changes are made
  • gulp --production — Compile assets for production (no source maps).

General Sage Documentation

Learn more about how the Sage starter theme works in general by studying its documentation. Remember that currently we are working from version 8.5.0 of Sage; version 9+ handles things differently.

Accessibility Testing

The Torgersen theme is designed to be modified by units on campus to suit their own needs, but has been designed to help ensure that any modifications made to the theme meet the same same high standards for accessibility as the original. To this end, accessibility testing functionality has been integrated into the theme's build system with a couple of gulp tasks:

Possible Workflow

The workflow for using these commands might be something like this:

  1. Commit changes to your repository, which triggers gulp scan. This basically gives a report that says, "You are committing a file whose filename has matched a defined test suite. We've run scans on the following pages that are built from or use this file, and here are a list of accessibility rules that have been violated." This gives you a quick sense that something is broken.
  2. To then see that problem in context, you would run gulp scanPage on a particular URL that was flagged in gulp scan. This will load the page in the browser and display a widget that shows the results of scans associated with that page. The user can see all those reports in context on the page that triggered them, and, by clicking on a violation that is listed in a report, have the part of the page that triggered the violation be highlighted.

gulp scan

When the user commits changes to the repository, the gulp scan task is called, running some Mocha tests. These tests work by loading pages in the PhantomJS headless browser and use Selenium's WebDriverJS to dynamically inject the aXe JavaScript accessibility testing library into these pages. aXe will scan specific sections of relevant pages and the results of these tests will be output to the console and will also be published as a web page to test/reports using the MochAwesome reporter.

Exactly what scans get run is determined by the configuration file, test/specs/full.json. Basically, gulp scan will look at each file that has changed in this commit and see if it matches any of the selectors for test suites specified in the configuration file. Each test suite, can be triggered by matching either an exact file name or a regular expression. Suites designed to be triggered based on an exact file name match are stored in the files array, while suites to be triggered based on a regular expression match are stored in the expressions array. For each test suite, one or more web pages can be scanned based on page objects defined in the suite. The options object, describing which accessibility rules will be checked against, and the context object, describing which parts of the page will be examined, are both specified in the conf object associated with the page. The syntax of the context and options objects is based on the aXe Javascript Accessibility API's context parameter, and options parameter, respectively.

File Match Example

In test/specs/full.json, a file test suite may be defined that matches a particular filename, by setting the suite's match property to the path to that file from the root of the project. For instance, for a particular commit, the file, search.php, may have changed and there may be a suite in the full configuration whose match property matches that filename.

{
  "files": [
    {
      "match": "search.php",
      ...
    }
  ]
}

Since search.php is used whenever the home page is accessed with the "s" URL parameter, we will want to create a page object in that test suite that matches that URL, so that a scan is run on that page. I know, given the Wordpress unit test data, that a search for "test" will return a list of results, so my first page object matches the URL with that string as the search query.

{
  "files": [
    {
      "match": "search.php",
      "pages": [
        {
          "match": "/s=test"
          ...
        }
      ]
    }
  ]
}

However, I also know that search.php returns a different view if my query string doesn't return any results, so I'll need to add a page object to match that too. In that case, I would set the "s" URL parameter to the garbage string, "kjhgkjhgkj", which I know won't return anything.

{
  "files": [
    {
      "match": "search.php",
      "pages": [
        {
          "match": "/s=test"
          ...
        },
        {
          "match": "/s=kjhgkjhgkj"
          ...
        }
      ]
    }
  ]
}

Also, in this case, in order to get the most useful feedback, we will only want to scan the relevant context, in this case, the part of the page that is generated by search.php, which is contained in the element with the id, "#template-search". Similarly, we will not need to run certain tests, such as whether the page has a title, since that is set in the <head> tag, which is beyond the scope of what search.php affects. Again, what sections of the page to include and exclude and what specific rules to test for is determined by the conf object.

{
  "files": [
    {
      "match": "search.php",
      "pages": [
        {
          "match": "/s=test",
          "conf": {
            "context": {
              "include": [["#template-search"]]
            },
            "options": {
              "rules": {
                "document-title": { "enabled": false },
                "meta-refresh": { "enabled": false },
                "meta-viewport-large": { "enabled": false },
                "meta-viewport": { "enabled": false }
              }
            }
          }
        },
        ...
      ]
    },
    ...
  ]
}

For details of how to specify what parts of the page to scan and what rules to include, see the documentation for the axe-core library'scontext parameter and options parameter. Generally, if it can be serialized to a JSON object, you can use the syntax specified there.

Templates

Some configurations will be the same for many scans so, rather than create duplicate conf objects for all of them, we can create a single template object defining a conf object common to all of them. Any page objects that reference that template will inherit its conf object properties. Templates are defined in the templates array in the configuration file. A page references a template by setting its template property to the value of the template's match property.

Note: The word "template" as used when referring to the template objects in the templates section of the config file has nothing to do with way the word "template" is used in common Wordpress parlance. They are separate concepts.

As an example, many scans will want to use, as their base, rules tagged as being associated with "WCAG 2.0 A", "WCAG 2.0 AA", and "Best Practice". We can create a template called "base" that defines these base rules that are to be used and have our page object reference that.

Base Template
{
  "files": [
    ...
  ],
  "templates": [
    {
      "match": "base",
      "conf": {
        "options": {
          "runOnly": {
            "type": "tag",
            "values": ["wcag2a", "wcag2aa", "best-practice"]
          }
        }
      }
    }
  ]
}
Page That References Template
{
  "files": [
    {
      "match": "templates/footer.php",
      "pages": [
        {
          "match": "/",
          "template": "base",
          "conf": {
            "context": {
              "include": [["footer#vt_footer_wrapper"]]
            },
            "rules": {
              "document-title": { "enabled": false }
            }
          }
        }
      ]
    }
  ],
  "templates": [
    ...
  ]
 }

In our example, we still have a conf object associated with the page object, even though that page object is also referencing the template. In this case, the conf object contained in the page object supplements the conf object in the template, by specifying that the "document-title" rule is to be excluded from the scan. It also limits the context so that only the contents of our page's primary footer tag will be scanned.

Templates matching templates

page objects can reference template objects, but template objects can also reference other template objects, inheriting properties from them. Knowing this, we could eliminate the conf object from the page object altogether and just reference a "footer" template object, which in turn inherits from the "base" template.

{
  "files": [
    ...
    "match": "templates/footer.php",
    "pages": [
      {
        "match": "/",
        "template": "footer"
      },
      ...
    ]
  ]
  "templates": [
    {
      "match": "footer",
      "template": "base",
      "conf": {
        "context": {
          "include": [["footer#vt_footer_wrapper"]]
        },
        "rules": {
          "document-title": { "enabled": false }
        }
      }
    },
    {
      "match": "base",
      "conf": {
        "options": {
          "runOnly": {
            "type": "tag",
            "values": ["wcag2a", "wcag2aa", "best-practice"]
          }
        }
      }
    }
  ]
}

Regular Expression Match Example

In our previous examples, we showed how a suite of tests can be triggered by a filename match, but you can also use a regular expression to trigger a match. For instance, you can write a regular expression that matches any file ending in "scss", "/^(?!\\.\\.\\/).*\\.scss$/i", and any time one of those files change, you can scan a representative sample of pages using a template that looks at the whole page, but only tests rules that would be affected by styling changes, such as "color-contrast".

{
  "expressions": [
    {
      "match": "/^(?!\\.\\.\\/).*\\.scss$/i",
      "pages": [
        {
          "match": "/about/page-markup-and-formatting/",
          "template": "scss"
        }
      ]
    }
  ],
  "templates": [
    {
      "match": "scss",
      "conf": {
        "options": {
          "runOnly": {
            "type": "rule",
            "values": ["button-name", "color-contrast", "link-in-text-block", "link-name"]
          }
        }
      }
    }
  ]
}
Setting Regular Expression Suites to Only Run Once

Depending on your purposes, for a particular set of file names in a commit, you may want a regular expression suite to only be run the first time a file in that set is matched, or every time. This option is set using the once property in the regular expression suite. So, for instance, if you wanted to run a suite of tests of JavaScript-related functionality only once, even if there were multiple JavaScript files in your changeset, you would set once to the boolean value true, like so:

    {
      "match": "/^(assets\\/scripts\\/).*\\.js$/i",
      "once": true,
      "pages": [
        {
          "match": "/",
          "template": "js"
        }
      ]
    }

The default value for once is false, so, unless otherwise specified, a regular expression suite will be run every time it is matched. File suites, by contrast, do not have a once property and are always run only once.

gulp scanPage

In addition to running scans automatically based on files that have changed since the last commit to the master branch, the user can choose to run a scan on a particular page using the gulp scanPage command passing in the match parameter for the page to be scanned. So, to scan the home page, you would run the following:

gulp scanPage --match=/

Running this type of scan looks through all the suites in the full config file for any page objects whose match property is the URL referenced and, for each one, runs a scan based on its associated conf object. Thus, several separate scans may be performed on a single page, each taken from a separate suite. For instance, there may be one that scans the footer with a certain set of rules, one that scans the header with a different set of rules, and so forth. When this type of scan is run, the specified URL is opened in the browser and the results displayed as plugins of the tota11y accessibility visualization toolkit, a collapsible widget that is injected into the page.