Meeting WCAG requirements for cardLayout 'buttons'

We are using a cardLayouts as a stack of buttons for actions the user must take before submitting the form.  The cardLayout has a label informing the user the type of action to perform and a status indicator to show the user visually whether the task is complete.  When you click the cardLayout, it displays a subpanel of information is displayed for the user to perform the needed task.

Ultimately, we may have 7 – 10 of these cardLayouts stacked on the page.  This design allows users to see only what they want to see when they want to see it.  It also, helps them know when all steps are completed.  We feel that this is a very appealing UI design.

 

We are facing a challenge meeting WCAG requirements for this design.  Specifically, Criterion 4.1.2 which requires the following:

4.1.2 Name, Role, Value: For all user interface components (including but not limited to: form elements, links and components generated by scripts), the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to user agents, including assistive technologies. (Level A)

Taken from w3.org.

 

These cardLayouts essentially are like collapsible sectionLayouts but with a status indicator (and maybe other data we add as needed).  To meet WCAG 4.1.2, the cardLayout needs to include certain ARIA attributes to show that it owns/controls other page controls and indicate whether its current expanded/collapsed status.  A sectionLayout control has the aria-expanded attribute but does not use aria-owns or aria-controls.  The section layout, thus, does not fully meet WCAG 4.1.2.

 

The question is…. How can we add the appropriate aria tags into the page HTML for these cardLayouts so they meet the WCAG 4.1.2 standard for programmatic determination of states?  We need aria-controls to point to the container for the child controls (i.e. sectionLayout, columnsLayout, whatever) and aria-expanded to indicate the current shown/hidden state (like what a sectionLayout currently does).

 

 

a!localVariables(
  local!statuses: {
    a!map(
      name: "Not Started",
      icon: "circle-o-large",
      color: "#2B3A44"
    ),
    a!map(
      name: "Started",
      icon: "adjust",
      color: "#1931E3"
    ),
    a!map(
      name: "Completed",
      icon: "check-circle",
      color: "POSITIVE"
    )
  },
  local!currentSatus: "Not Started",
  local!showChild: false,
  {
    a!cardLayout(
      contents: a!sideBySideLayout(
        items: {
          a!sideBySideItem(
            item: a!richTextDisplayField(
              labelPosition: "COLLAPSED",
              value: {
                a!richTextItem(
                  text: "Step 1 label (w/ child controls)",
                  color: "#007958",
                  size: "MEDIUM",
                  style: { "STRONG" }
                )
              },
              align: "LEFT",
              marginAbove: "NONE",
              marginBelow: "NONE"
            )
          ),
          a!sideBySideItem(
            item: a!richTextDisplayField(
              labelPosition: "COLLAPSED",
              value: a!localVariables(
                local!statusIndex: wherecontains(
                  tostring(local!currentSatus),
                  local!statuses.name
                ),
                local!currentStatus: index(local!statuses, local!statusIndex),
                a!richTextIcon(
                  icon: index(local!currentStatus, "icon"),
                  caption: index(local!currentStatus, "name"),
                  color: index(local!currentStatus, "color"),
                  size: "MEDIUM"
                )
              ),
              align: "RIGHT",
              marginAbove: "NONE",
              marginBelow: "NONE"
            ),
            width: "MINIMIZE"
          )
        }
      ),
      link: a!dynamicLink(
        value: not(local!showChild),
        saveInto: local!showChild
      ),
      style: "STANDARD"
    ),
    a!columnsLayout(
      columns: {
        a!columnLayout(width: "EXTRA_NARROW"),
        a!columnLayout(
          contents: {
                a!richTextDisplayField(value: "Hello, World.  Here is where a bunch of controls will live.")
          }
        )
      },
      showWhen: local!showChild,
      marginAbove: "STANDARD"
    ),
    a!sectionLayout(),
    a!cardLayout(
      contents: a!sideBySideLayout(
        items: {
          a!sideBySideItem(
            item: a!richTextDisplayField(
              labelPosition: "COLLAPSED",
              value: {
                a!richTextItem(
                  text: "Step 2 label (no children, illustrative only)",
                  color: "#007958",
                  size: "MEDIUM",
                  style: { "STRONG" }
                )
              },
              align: "LEFT",
              marginAbove: "NONE",
              marginBelow: "NONE"
            )
          ),
          a!sideBySideItem(
            item: a!richTextDisplayField(
              labelPosition: "COLLAPSED",
              value: a!localVariables(
                local!statusIndex: wherecontains(
                  tostring(local!currentSatus),
                  local!statuses.name
                ),
                local!currentStatus: index(local!statuses, local!statusIndex),
                a!richTextIcon(
                  icon: index(local!statuses[2], "icon"),
                  caption: index(local!statuses[2], "name"),
                  color: index(local!statuses[2], "color"),
                  size: "MEDIUM"
                )
              ),
              align: "RIGHT",
              marginAbove: "NONE",
              marginBelow: "NONE"
            ),
            width: "MINIMIZE"
          )
        }
      ),
      style: "STANDARD"
    ),
    a!sectionLayout(),
    a!cardLayout(
      contents: a!sideBySideLayout(
        items: {
          a!sideBySideItem(
            item: a!richTextDisplayField(
              labelPosition: "COLLAPSED",
              value: {
                a!richTextItem(
                  text: "Step 3 label (no children, illustrative only)",
                  color: "#007958",
                  size: "MEDIUM",
                  style: { "STRONG" }
                )
              },
              align: "LEFT",
              marginAbove: "NONE",
              marginBelow: "NONE"
            )
          ),
          a!sideBySideItem(
            item: a!richTextDisplayField(
              labelPosition: "COLLAPSED",
              value: a!localVariables(
                local!statusIndex: wherecontains(
                  tostring(local!currentSatus),
                  local!statuses.name
                ),
                local!currentStatus: index(local!statuses, local!statusIndex),
                a!richTextIcon(
                  icon: index(local!statuses[3], "icon"),
                  caption: index(local!statuses[3], "name"),
                  color: index(local!statuses[3], "color"),
                  size: "MEDIUM"
                )
              ),
              align: "RIGHT",
              marginAbove: "NONE",
              marginBelow: "NONE"
            ),
            width: "MINIMIZE"
          )
        }
      ),
      style: "STANDARD"
    )
  }
)

  Discussion posts and replies are publicly visible

  • 0
    Certified Lead Developer

    So I'm not too familiar with Appian accessibility, but I've been doing some research that might be able to help.

    Basically, I found this webpage that documents how Appian meets certain ARIA requirements, but I can't find anything on which components use aria-owns or aria-controls. I will say that if the sectionLayout doesn't meet your needs (in having Appian generate those tags), then I'd recommend either getting rid of the collapsible section layouts (thus removing the need for them) or experimenting around with other Appian interface components to see if they generate the correct aria tags.

    For example, you could change the section layout to have two different version, one that is an actual section layout (with the expanded contents, but with isCollapsible=false), and then another that is actually a richTextComponent that mimics the look of a collapsed section layout. You would then toggle between displaying one or the other based on some button or dynamic link that functions as a stand in for ability to collapse section layouts using the built-in functionality, which would then itself have the accessibility text added.

    I don't know if those components (the added button or dynamic link, I mean) would actually meet the ARIA tag requirements you have, though.

  • You can add aria-expanded attribute by simply adding angle-down and angle-right icons to your cardLayout  to indicate if a control is expanded or collapsed.

          

    a!localVariables(
      local!statuses: {
        a!map(
          name: "Not Started",
          icon: "circle-o-large",
          color: "#2B3A44"
        ),
        a!map(
          name: "Started",
          icon: "adjust",
          color: "#1931E3"
        ),
        a!map(
          name: "Completed",
          icon: "check-circle",
          color: "POSITIVE"
        )
      },
      local!isExpanded: false,
      local!currentSatus: "Not Started",
      local!showChild: false,
      {
        a!cardLayout(
          contents: a!sideBySideLayout(
            items: {
              a!sideBySideItem(
                item: a!richTextDisplayField(
                  labelPosition: "COLLAPSED",
                  value: a!richTextIcon(
                    icon: if(
                      local!isExpanded,
                      "angle-right",
                      "angle-down"
                    ),
                    size: "MEDIUM"
                  )
                ),
                width: "MINIMIZE"
              ),
              a!sideBySideItem(
                item: a!richTextDisplayField(
                  labelPosition: "COLLAPSED",
                  value: {
                    a!richTextItem(
                      text: "Step 1 label (w/ child controls)",
                      color: "#007958",
                      size: "MEDIUM",
                      style: { "STRONG" }
                    )
                  },
                  align: "LEFT",
                  marginAbove: "NONE",
                  marginBelow: "NONE"
                )
              ),
              a!sideBySideItem(
                item: a!richTextDisplayField(
                  labelPosition: "COLLAPSED",
                  value: a!localVariables(
                    local!statusIndex: wherecontains(
                      tostring(local!currentSatus),
                      local!statuses.name
                    ),
                    local!currentStatus: index(local!statuses, local!statusIndex),
                    a!richTextIcon(
                      icon: index(local!currentStatus, "icon"),
                      caption: index(local!currentStatus, "name"),
                      color: index(local!currentStatus, "color"),
                      size: "MEDIUM"
                    )
                  ),
                  align: "RIGHT",
                  marginAbove: "NONE",
                  marginBelow: "NONE"
                ),
                width: "MINIMIZE"
              )
            }
          ),
          link: a!dynamicLink(
            value: not(local!showChild),
            saveInto: {
              local!showChild,
              a!save(local!isExpanded, not(local!isExpanded))
            }
          ),
          style: "STANDARD"
        ),
        a!columnsLayout(
          columns: {
            a!columnLayout(width: "EXTRA_NARROW"),
            a!columnLayout(
              contents: {
                a!richTextDisplayField(
                  value: "Hello, World.  Here is where a bunch of controls will live."
                )
              }
            )
          },
          showWhen: local!showChild,
          marginAbove: "STANDARD"
        ),
        a!sectionLayout(),
        a!cardLayout(
          contents: a!sideBySideLayout(
            items: {
              a!sideBySideItem(
                item: a!richTextDisplayField(
                  labelPosition: "COLLAPSED",
                  value: {
                    a!richTextItem(
                      text: "Step 2 label (no children, illustrative only)",
                      color: "#007958",
                      size: "MEDIUM",
                      style: { "STRONG" }
                    )
                  },
                  align: "LEFT",
                  marginAbove: "NONE",
                  marginBelow: "NONE"
                )
              ),
              a!sideBySideItem(
                item: a!richTextDisplayField(
                  labelPosition: "COLLAPSED",
                  value: a!localVariables(
                    local!statusIndex: wherecontains(
                      tostring(local!currentSatus),
                      local!statuses.name
                    ),
                    local!currentStatus: index(local!statuses, local!statusIndex),
                    a!richTextIcon(
                      icon: index(local!statuses[2], "icon"),
                      caption: index(local!statuses[2], "name"),
                      color: index(local!statuses[2], "color"),
                      size: "MEDIUM"
                    )
                  ),
                  align: "RIGHT",
                  marginAbove: "NONE",
                  marginBelow: "NONE"
                ),
                width: "MINIMIZE"
              )
            }
          ),
          style: "STANDARD"
        ),
        a!sectionLayout(),
        a!cardLayout(
          contents: a!sideBySideLayout(
            items: {
              a!sideBySideItem(
                item: a!richTextDisplayField(
                  labelPosition: "COLLAPSED",
                  value: {
                    a!richTextItem(
                      text: "Step 3 label (no children, illustrative only)",
                      color: "#007958",
                      size: "MEDIUM",
                      style: { "STRONG" }
                    )
                  },
                  align: "LEFT",
                  marginAbove: "NONE",
                  marginBelow: "NONE"
                )
              ),
              a!sideBySideItem(
                item: a!richTextDisplayField(
                  labelPosition: "COLLAPSED",
                  value: a!localVariables(
                    local!statusIndex: wherecontains(
                      tostring(local!currentSatus),
                      local!statuses.name
                    ),
                    local!currentStatus: index(local!statuses, local!statusIndex),
                    a!richTextIcon(
                      icon: index(local!statuses[3], "icon"),
                      caption: index(local!statuses[3], "name"),
                      color: index(local!statuses[3], "color"),
                      size: "MEDIUM"
                    )
                  ),
                  align: "RIGHT",
                  marginAbove: "NONE",
                  marginBelow: "NONE"
                ),
                width: "MINIMIZE"
              )
            }
          ),
          style: "STANDARD"
        )
      }
    )

  • I can try that, but it will only be half the solution.

    How do you get aria-owns/aria-controls added?  aria-expanded by itself does not fully meet the WCAG standard (this is the problem the sectionLayout has.... it has aria-expanded but does not indicate aria-owns/aria-controls).

  • I think this will only move the problem from the cardLayout to the button/link that toggles between the sectionLayout and richTextComponent.  The button/link would also need to use aria-controls since it changes the state of the other two.

  • 0
    Certified Lead Developer
    in reply to stevenh6272

    I suggest to contact Appian for any details. We, as Appian developers, only have the options provided by the components.

  • I figured that would be the recommendation.

    Thanks.