Reworking of Accessible Helper Module

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
johnbarclay's picture

I reworked the accessible helper module into an api and a fix and help module. The documentation is below and does an ok job of showing what I am going for. Its more of a proof of concept than an alpha version, but I think it works. I'm definatley looking for feedback. I made an short video also. http://www.youtube.com/watch?v=VZkccMbt7CQ The module is at http://johnbarclay.com/accessible.zip until I get it checked in.

The gist of the reworkin is have an api module that lets the site admin set their preferences for the site and get these preferences to the theming layer via variables and to other modules via api functions. This allows a theme to support aria roles, but allow site admins to turn them off. It also allows interfaces such as the block admin configure to provide fields for aria roles, etc.

Accessible Helper Modules is a group of modules to
make creating accessible websites easier.

At this point it is more of a proof of concept than a useful module.

The modules' functionality are
divided up as such:

Accessible API Module (accessible_api)

General toolset including guidelines data, site accessibility preferences,
and functions other modules may build on.

Functionality

  • store any reusable data such as guidelines and mapping of guidelines
    to drupal authoring contexts.
  • configuration interface and retrieval function for site accessibility guideline preferences.
    The idea here is store the general accessibility preferences here, but utilize
    preferences in other modules and themes.
  • configuration of accessibility conventions preferences where practices are not yet clear.
    e.g. should content be positioned off screen left, top, or right

Accessible Fix Module (accessible_fix)

Accomodate and fix accessiblilty deficiencies
in core and contributed module. Works on a case by case basis with hooks like
hook_form_alter to modify other modules forms and markup. As a rule, these
are either stop gap measures until real fixes are made in modules or when
accessibility implementation is unclear or preference based.

Functionality

  • examples include google_cse and search module in drupal 6 needing labels.
  • allows for offscreen heading configuration in blocks
  • allows for applying aria roles to blocks

Accessible Help Module (accessible_help)

Adds contexttual accessibility help for content authors.

  • provides contextual help through hook_help

examples folder

Much of accessiblity is on the theming layer, so example .tpl and template.php overrides
are included in the examples directory of this module.

Install and Configure

  1. Enable the module(s) you want to use. All require accessible_api. (admin/build/modules/list)
  2. Give site admins permissions to "administer accessible module" (admin/user/permissions)
  3. Configure the accessible_api module. (admin/settings/accessible)
  4. Configure Accessible Fixes Module (admin/settings/accessible/accessible_fix)

At this point, nothing has changed in the HTML of your site unless you have a theme that is
already altered to leverage this module.
The following template changes will allow your setting to
affect the HTML.

Working with block.tpl

Blocks can leverage the accessible_api and accessible_fix by adding aria roles and positioning headers off
screen.

Positioning block headers off screen

  1. Enable offscreen headers. A checkbox for "Class this block's header tag as "offscreen" should be there
    if "offscreen block headings" has been checked in Accessible Fix Settings (admin/settings/accessible/accessible_fix).
  2. Alter the block.tpl (this is in examples/genesis_aria/templates/block/block.tpl)

    from

    <h2><?php print $block->subject ?>
    

    to

    <h2 <?php if ($block->accessibility->offscreen) { print 'class="offscreen"';}?>><?php print $block->subject ?></h2>
    
  3. Configure some blocks to have offscreen headers. Navigate to block configure (admin/build/block/list/[theme])
    and select the configure link.
  4. Flushing the theme cache may be necessary until this module is farther along.

Adding ARIA Roles to the block

  1. Make sure you have enabled some role goupings such as landmarks, document dtructure,
    widget, abstract (admin/settings/accessible). This controls the roles available.
  2. Alter the block.tpl (this is in examples/genesis_aria/templates/block/block.tpl)

    from

    <div class="block-inner">
    
    

    to

    <div class="block-inner" <?php
        if ($block->accessibility->aria_attributes) {print 'role="'. $block->accessibility->aria_attributes .'"' ;} ?>
    >
    
  3. Configure blocks to have aria roles. Navigate to block configure (admin/build/block/list/[theme])
  4. Flushing the theme cache may be necessary until this module is farther along

Comments

Looking at adding aria roles

mgifford's picture

Thanks so much for this John. In your example (in the last release anyways) you didn't have a genesis theme, just garland.

mike@dev:~/dm6/sites/aebc/themes/aebc$ ls ../../../all/modules/accessible/examples/garland/
block.tpl.php change_summary.txt garland.patch node.tpl.php page.tpl.php

So I took from the block.tpl.php and added the aria PHP you provided above.

I changed it a bit because I'd rather make a new module like this future compatible. D7 is going with element-invisible & element-hidden so would rather have as much consistency as possible (as there is already a lot of confusion on this topic).

<?php
// $Id: block.tpl.php,v 1.1 2009/02/05 04:26:23 johnbarclay Exp $
?>

<div id="block-<?php print $block->module .'-'. $block->delta; ?>" class="clear-block block block-<?php print $block->module ?>"
  <?php if ($block->accessibility->aria_attributes) {print 'role="'. $block->accessibility->aria_attributes .'"' ;} ?> >
<?php if (!empty($block->subject) && $block->module != 'menu'): ?>
  <h2 <?php if ($block->offscreen) { print 'class="element-invisible"';}?>><?php print $block->subject ?></h2>
<?php endif;?>

  <div class="content"><?php if ($block->module == 'menu' && !empty($block->subject)): ?>
  <h2 <?php if ($block->offscreen) { print 'class="element-invisible"';}?> ><?php print $block->subject ?></h2>
<?php endif;?><?php print $block->content ?></div>
</div>

In a Zen sub-theme the node.tpl.php would largely boil down to changing this:

      <?php if ($terms): ?>
        <div class="terms terms-inline"><h2 class="element-invisible"><?php print t('Related Terms'); ?></h2><?php $terms; ?></div>
      <?php endif; ?>

and

<?php if (!empty($links)) { ?>
   <div class="links"><h2 class="element-invisible"><?php print t('Related Pages'); ?></h2><?php print $links; ?></div>
  <?php } ?>

With the page.tpl.php it's just (as there is no $tabs2 variable in Zen)

          <?php if ($tabs): ?>
              <h2 class="element-invisible">'<?php t('Tasks'); ?></h2>
              <div class="tabs"><?php print $tabs; ?></div>
            <?php endif; ?>

I do hope that we can get some of these into popular themes in D6. A lot of these changes don't need to wait till a D7 release.

Feel Free to go with this...

johnbarclay's picture

Mike. You have CVS rights to the project. And I won't be working on it this week. So feel free to make any of these changes especially in the samples section.

I'm not sure if I like the $block->accessibility->aria_attributes syntax since something like $block->accessibility_aria_attributes is less likely to break a theme if the accessible_api is turned off.

I was thinking with d6, since a .module can designate what .tpl files to use via the preprocess hooks, have accessible_fix module allow a site admin to actually use those .tpl files in the examples folder without messing around in the filespace. This would give the site admins that weren't themers or coders a chance at an accessible site. Then we could create the .tpl (block.tpl, user-login.tpl, etc) for each of the core themes and just let the admin override them with the a11y tweaked ones.

D7 Classing Approach

johnbarclay's picture

I took a look at a11y title and the drupal 7 approach to classes, ids, and attributes in theming (http://drupal.org/node/254940#html-class-variable). I also looked at the element-invisible class. Thanks for pointing these out.

I ended up toying with the following changes:

  • All the wording, classing, and variable names from offscreen to invisible or element-invisible for clarity
  • Emulated the drupal 7 preprocess and "second phase processor" in accessible_fix_preprocess_block(&$variables)
    in order to deliver d7 style variables to the themeing layer. This isn't really for forward compatibility,
    I just like the approach in D7.

    function accessible_fix_preprocess_block(&$variables) {

      if($variables['block']->accessibility) {
        $accessibility = unserialize($variables['block']->accessibility);
        $aria_roles = array_keys($accessibility['aria_roles']);
       
        // 1. make accessibility variables available to block for theming
        $variables['block']->accessibility = new stdClass;
        foreach($accessibility as $key => $value) {
          $variables['block']->accessibility->{$key} = $value;
        }
        $variables['block']->accessibility->aria_attributes = join(" ", $aria_roles);
       
        // 2. get data into block array data to emulated d7 approach.
        // http://drupal.org/node/254940#html-class-variable
       
        // 2.1 add block-title class if its not there already.
        if (!$variables['title_attributes_array']['class'] ||
            !in_array('block-title',$variables['title_attributes_array']['class'])) {
          $variables['title_attributes_array']['class'][] = 'block-title';
        }
        // 2.2 add element-invisible class if author has marked as such
        if ($variables['block']->accessibility->element_invisible) {
          $variables['title_attributes_array']['class'][] = 'element-invisible';
        }

        // 2.3 add aria roles if present
        foreach ($aria_roles as $role) {
          $variables['content_attributes_array']['role'][] = $role;
        }
       
       
        /** 3. generete $content_attributes and $title_attributes vars
         to emulate d7 approach.  Act like a d7 second phase processor
         for arrays in step 2 above.
         **/
       
       $variables['content_attributes'] .= accessible_fix_flatten_attr($variables['content_attributes_array']);
       $variables['title_attributes'] .= accessible_fix_flatten_attr($variables['title_attributes_array']);

       // dpm('preprocessed block variables'); dpm($variables);
      }
     
     
    }

    function accessible_fix_flatten_attr($attr_array = NULL) {
      if ($attr_array) {
        foreach ($attr_array as $attr_name => $attrs) {
          $flattenned .= " $attr_name=\"" . join(" ",$attrs) .'"';
        }
      }
      return $flattenned;
    }

    This change allowed the .tpl file to be easier to work with

    <div id="<?php print $block_id; ?>" class="<?php print $classes; ?>">
      <div class="block-inner">
        <?php if ($block->subject): ?><h2<?php print $title_attributes; ?>><?php print $block->subject ?></h2><?php endif; ?>
        <div class="block-content"<?php print $content_attributes; ?>><?php print $block->content ?></div>
        <?php print $edit_links; ?>
      </div>
    </div> <!-- /block -->

    Though an author that wants more control over attributes can still do like you did:

    <div id="block-<?php print $block->module .'-'. $block->delta; ?>" class="clear-block block block-<?php print $block->module ?>"
      <?php if ($block->accessibility->aria_attributes) {print 'role="'. $block->accessibility->aria_attributes .'"' ;} ?> >
    <?php if (!empty($block->subject) && $block->module != 'menu'): ?>
      <h2 <?php if ($block->offscreen) { print 'class="element-invisible"';}?>><?php print $block->subject ?></h2>
    <?php endif;?>

      <div class="content"><?php if ($block->module == 'menu' && !empty($block->subject)): ?>
      <h2 <?php if ($block->offscreen) { print 'class="element-invisible"';}?> ><?php print $block->subject ?></h2>
    <?php endif;?><?php print $block->content ?></div>
    </div>

Looking for feedback on this.

Above Changes Made and Committed

johnbarclay's picture

I ended up making the above changes and they are committed to head.

I also ended up making the accessible fixes for modules such as google_cse and search more modular so each one is in a separate file. Kinda like the userpoints module does, but without each file being a separate module. This will make collaboration easier.

I also made it so the admin could override a template such as garland/block.tpl or genesis/block.tpl with an accessible alternative via the web interface alone. This has some utility for sites that haven't modified their themes.

Great!

mgifford's picture

Hey John,

I just verified that I can indeed write to the CVS. I added a README.txt file & a block.tpl.php file in a new Zen directory.

Very much like the switch to element-invisible, thanks.

I did wonder what happened to the examples within the garland & genesis_aria directories. They no longer seem to be included.

I haven't had much of a chance to play with it more, but did upload the latest code from the CVS for testing.

Great concept

Frank Ralf's picture

Hi John,

I like the API concept. Hope to find the time to have a closer look soon.

Cheers,
Frank

D7 Version

johnbarclay's picture

I attached a zip of the d7 version in the issue queue at:

http://drupal.org/node/804084

A quick walkthough is at:

http://blip.tv/file/3647172

I added more of a plugin approach to the accessible_fix module.

Here's a rough rationale from the developers readme file:

Accessible Fix readme for developers.

I. ARCHITECTURE RATIONALE

The module is designed for short term, small fixes so code is in many small, modular files
to allow for simple patching. Its also designed for code execution and admin options to be limited to only enabled modules.

I. OVERVIEW

A. Accessible Fix leverages the preferences provided by Accessible API when possible.
B. It is meant as a stop gap measure until issues are resolved in core and contrib modules.
C. It genertally intevenes in other modules through the theming layer or hooks such as hook_form_alter.
D. Code in it should be designed to fail gracefully when the module it is accomodating is repaired or changes.

II. HOW MODULE FIXES WORK and HOW YOU MIGHT WRITE ONE

All individual module fixes are stored in the directory module_fixes directory and have a set of files such as MODULE.inc, MODULE.admin.inc, MODULE.documentation.inc, etc.

  • MODULE.inc. Required. This file is loaded if a module with the same name is enabled...it should have as little code as possible in it. The MODULE.inc file should have any hooks in it.

  • MODULE.admin.inc should have any administrative interfaces in it. In particular, the functions accessible_fix_admin_module_fixes_MODULE and accessible_fix_admin_module_fixes_MODULE_submit are called to gather form elements for the configuration form.

If you would like to add to the admin form, its called "accessible_fix_admin_edit" so it can be altered with hook_form_FORM_ID_alter via a function named MODULE_form_accessible_fix_admin_edit_alter()

III. ADVOCACY

Module fixes should include a file called MODULE.help.inc that includes more information about the issue,
proposed patches, etc. This will be available as a link from the admin form and at accessible_fix/help.
This type of information is best kept in public places like http://drupal.org/node/425494 where it can be
kept up to date and referenced from MODULE.help.inc.