Skip to content

seb-jean/stimulus_aria_widgets

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

StimulusAriaWidgets

Short description and motivation.

Why?

Stimulus ARIA Widgets sit in the middle of the spectrum between entirely JavaScript-based components (like React, for example) and pre-packaged server-generated components.

Stimulus controllers decorate client-side behavior onto server-generated documents by targeting elements through data--prefixed attribute annotations.

Stimulus is entirely client-side, and Rails is entirely server-side. Generating the HTML with the appropriate annotations is both extremely particular and entirely the responsibility of the server.

Installation

Add the stimulus_aria_widgets dependency to your application's Gemfile:

gem 'stimulus_aria_widgets', github: 'seanpdoyle/stimulus_aria_widgets', branch: 'main'

Additionally, depend on action_view-attributes_and_token_lists:

gem 'action_view-attributes_and_token_lists', github: 'seanpdoyle/action_view-attributes_and_token_lists', branch: 'main'

And then execute:

$ bundle

Once the gem is installed, add the client-side dependency to your project via npm or Yarn:

yarn add https://github.com/seanpdoyle/stimulus_aria_widgets.git

Installing Polyfills

The DialogController relies on functioning <dialog> elements and the [inert] attribute. If your application needs to polyfill that element, install the transitive dependencies:

yarn add wicg-inert dialog-polyfill

Then import the polyfill:

import "wicg-inert"
import { installPolyfills } from "@seanpdoyle/stimulus_aria_widgets"

installPolyfills(document)

Usage

The Accessible Rich Internet Applications Authoring Practices 1.1 provide guidance for implementing commonly occurring web widgets in accessible ways.

This engine aims to provide server-side helpers that generate HTML with the appropriate attributes to correspond with a suite of client-side Controllers.

The majority of the sever-generated attributes are [data-*] or [aria-*] prefixed, with the exception of [role]. By default, the elements are unstyled and generated without [class][] attributes.

Each instance constructed by the aria helper is an instance of Attributes containing TokenList instances.

Provided by the seanpdoyle/action_view-attributes_and_token_lists gem, Attributes and TokenList are Hash- and Set-like instances that can combine merge attribute and token values, render themselves to HTML-safe strings, or chain calls to #tag to construct HTML elements.

For more usage examples, read the project's System Tests and example template.

import { Application, Controller } from "stimulus"
import { ComboboxController } from "stimulus_aria_widgets"

const application = Application.start()
application.register("combobox", ComboboxController)

Helpers

aria.combobox embeds attributes on the root element:

  • data-controller="combobox"
  • data-action="input->combobox#expand"

Targets:

aria.combobox.combobox_target embeds attributes:

  • data-combobox-target="combobox"
  • data-action="keydown->combobox#navigate"
  • role="combobox"

aria.combobox.listbox_target embeds attributes:

  • data-combobox-target="listbox"
  • role="listbox"

aria.combobox.option_target embeds attributes:

  • role="option"
<form <%= aria.combobox %> data-turbo-frame="names">
  <label for="query">Names</label>
  <input id="query" <%= aria.combobox.combobox_target.merge aria: { expanded: params[:query].present? } %> type="search" name="query">

  <turbo-frame <%= aria.combobox.listbox_target %> id="names">
    <% if params[:query].present? %>
      <% %w[ Alan Alex Alice Barbara Bill Bob ].filter { |name| name.starts_with? params[:query] }.each_with_index do |name, id| %>
        <%= aria.combobox.option.tag.button name, type: "button", id: "name_#{id}", aria: { selected: id.zero? } %>
      <% end %>
    <% end %>
  </turbo-frame>
</form>

Actions

  • expand(InputEvent)

  • collapse(Event)

  • navigate(KeyboardEvent)

Toggling a <details> element

import { Application, Controller } from "stimulus"
import { DisclosureController } from "stimulus_aria_widgets"

const application = Application.start()
application.register("disclosure", DisclosureController)

Helpers

aria.disclosure(expanded_class:) embeds attributes on the root element:

  • data-controller="disclosure"
  • data-action="click->disclosure#toggle"
  • type="button"

When expanded_class: is provided, embeds:

  • data-disclosure-expanded-class
<button <%= aria.disclosure %> aria-controls="details">
  Open Details
</button>

<details id="details">
  <summary>Summary</summary>

  Details
</details>

Toggling the [hidden] attribute on an element

<button <%= aria.disclosure %> aria-controls="hidden">
  Toggle Hidden
</button>

<div id="hidden">Visible</div>

Toggling a CSS class on an element

<button <%= aria.disclosure expanded_class: "expanded" %> aria-controls="css-class">
  Toggle CSS class
</button>

<div id="css-class">CSS class</div>

Actions

  • toggle(Event)

Combined with a Disclosure, toggle a <dialog> element

import { Application, Controller } from "stimulus"
import { DialogController, DisclosureController } from "stimulus_aria_widgets"
import "stimulus_aria_widgets/polyfills"

const application = Application.start()
application.register("disclosure", DisclosureController)
application.register("dialog", DialogController)

Helpers

aria.dialog embeds attributes on the root element:

  • data-controller="dialog"
  • aria-model="true"
  • role="dialog"
<body>
  <main>
    <button <%= aria.disclosure %> aria-controls="dialog">
      Open Dialog
    </button>
  </main>

  <dialog <%= aria.dialog %> id="dialog">
    <h1 id="dialog-title">Modal Dialog</h1>

    <form action="/comments" method="post">
      <label for="body">Comment body</label>
      <textarea id="body" name="todo[body]"></textarea>

      <button>Submit</button>
      <button formmethod="dialog">Cancel</button>
    </form>
  </dialog>
</body>

Actions

  • showModal(Event)

  • close(Event)

import { Application, Controller } from "stimulus"
import { FeedController } from "stimulus_aria_widgets"

const application = Application.start()
application.register("feed", FeedController)

Helpers

aria.feed embeds attributes on the root element:

  • data-controller="feed"
  • data-action="keydown->feed#navigate"
  • role="feed"
<a href="#feed">Skip to #feed</a>

<div <%= aria.feed %> id="feed">
  <article>First article</article>
  <article>Second article</article>
  <article>Third article</article>
</div>

Actions

  • navigate(KeyboardEvent)

Configuration

By default, the widget builder will be available via the aria helper method.

To configure the name of the helper, override the config.stimulus_aria_widgets.helper_method value:

Rails.application.config.stimulus_aria_widgets.helper_method = :stimulus_builder

Contributing

Contribution directions go here.

License

The gem is available as open source under the terms of the MIT License.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Ruby 58.4%
  • TypeScript 23.1%
  • HTML 15.8%
  • JavaScript 1.4%
  • CSS 1.3%