Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI patterns showcase #1

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions ember-handbook/app/application/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@
Component Architecture
</a>
{{/let}}

{{#let (link "ui-patterns") as |link|}}
<a
href={{link.url}}
local-class={{if link.isActive "active"}}
{{on "click" link.transitionTo}}
>
UI Patterns
</a>
{{/let}}
</nav>
</header>

Expand Down
8 changes: 0 additions & 8 deletions ember-handbook/app/component-architecture/styles.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
.page {
display: flex;

& > main {
width: 35rem;
}
}

.spacer {
display: block;
height: var(--s-1);
Expand Down
2 changes: 1 addition & 1 deletion ember-handbook/app/components/article/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default class ArticleLayoutBasicComponent extends Component<
}

get slug(): string {
return slugify(this.title, { lower: true });
return this.title ? slugify(this.title, { lower: true }) : '';
}

get toc(): object {
Expand Down
14 changes: 14 additions & 0 deletions ember-handbook/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ Router.map(function () {
this.route('best-practices');
this.route('literature');
});

this.route('ui-patterns', function () {
this.route('general-considerations', function () {
this.route('considering-forms');
this.route('use-of-aria-live');
});
this.route('form-components', function () {
this.route('checkboxes', function () {
this.route('anti-patterns');
this.route('styling');
});
});
this.route('faqs');
})
});

export default Router;
20 changes: 20 additions & 0 deletions ember-handbook/app/ui-patterns/faqs/template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
description: >-
As this project identifies FAQs, they will be posted here. If you have a
question that is not answered here, please ask in the #topic-a11y channel in
the Ember Community Discord chat.
---

# FAQs

## Why don't you make this into an addon?

This is not an addon because each app will have different needs and requirements. Instead, this guide is intended to demonstrate best practices so teams can build the components that are right for their project. Additionally, an addon requires maintenance, which is a non-negligible set of work on its own.

## Why isn't \(insert component name here\) in this guide?

It's likely that your favorite component pattern isn't here yet because it hasn't been added yet. Or it's a carousel and those will never be here because they should never be used.

## You made a mistake! How do I tell you about it?

Thanks for catching it! Please file an issue here: [https://github.com/MelSumner/ember-component-patterns/issues](https://github.com/MelSumner/ember-component-patterns/issues).
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
description: >-
Anti-patterns produce outcomes that are ineffective because they are not
complete solutions, and as such are counterproductive. Developers are advised
to be aware of anti-patterns and avoid their use.
---

# Anti-patterns: Checkboxes

## Anti-pattern \#1: heading as checkbox group label

The use of the header element with a wrapper div around a group of checkboxes may be a tempting approach at first sight.

Consider this:

```markup
<div class="checkbox-group">
<h3>What are your preferred job titles?</h3>
<div class="form-group__checkbox">
<input id="c-00" type="checkbox" name="prefJob" value="ninja">
<label for="c-00">Ninja</label>
</div>
<div class="form-group__checkbox">
<input id="c-01" type="checkbox" name="prefJob" value="unicorn">
<label for="c-01">Unicorn</label>
</div>
<div class="form-group__checkbox">
<input id="c-02" type="checkbox" name="prefJob" value="rockstar">
<label for="c-02">Rockstar</label>
</div>
</div>
```

At first glance, all might seem fine. However, as assistive technology such as a screen reader will not associate the title and the options correctly, this approach should be considered an anti-pattern and should be avoided.

Instead, use the `<fieldset>` with a `<legend>` to successfully contain and associate the checkbox group. It should be remembered that the default styling for these elements can be overridden and should not be considered a blocker for their use.
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Checkboxes

## Introduction

Checkboxes are a useful input element when it is desirable to allow users to select more than one option from a short list of options. Checkboxes can have three states: checked, not checked, and indeterminate.

### User Expectations

Users expect the following to be true:

* it should be possible to navigate via keyboard
* animations should not be linked to functionality; if the user has disabled animations via system preferences, the checkbox should still be usable
* there should be a clear indicator of focus
* clicking on the label for the checkbox should also activate the checkbox itself \(this is done through properly [associating the label](../../general-considerations/considering-forms.md#labels) with the checkbox\)

## Part One: Considering Markup

Sometimes, a single checkbox is desirable. In these cases, a checkbox input only requires an associated label for the input:

```markup
<div class="form-group">
<input type="checkbox" id="input_checkbox" name="terms-confirmation" />
<label for="input_checkbox">I confirm that I have read the terms and conditions</label>
</div>
```

If the user should be able to select more than one option, a checkbox group should be used. To ensure that all of the checkboxes are associated with the single group, the `name` attribute value should be the same, and the checkbox group should be wrapped with the `<fieldset>` element:

```markup
<fieldset>
<legend>What kind of jobs are you interested in (select all that apply)?</legend>
<div class="form-checkbox">
<input type="checkbox" id="checkbox_job-preferences-00" name="jobPreferences" />
<label for="checkbox_job-preferences-00">Database Engineer</label>
</div>
<div class="form-checkbox">
<input type="checkbox" id="checkbox_job-preferences-01" name="jobPreferences" />
<label for="checkbox_job-preferences-01">CSS Engineer</label>
</div>
<div class="form-checkbox">
<input type="checkbox" id="checkbox_job-preferences-02" name="jobPreferences" />
<label for="checkbox_job-preferences-02">Accessibility Engineer</label>
</div>
<div class="form-checkbox">
<input type="checkbox" id="checkbox_job-preferences-03" name="jobPreferences" />
<label for="checkbox_job-preferences-03">Platform Engineer</label>
</div>
</fieldset>
```

### Keyboard Support

| Key | Function |
| :--- | :--- |

<table>
<thead>
<tr>
<th style="text-align:left"><code>TAB</code>
</th>
<th style="text-align:left">
<ul>
<li>Moves keyboard focus among the checkboxes</li>
</ul>
</th>
</tr>
</thead>
<tbody></tbody>
</table>

<table>
<thead>
<tr>
<th style="text-align:left"><code>SPACE</code>
</th>
<th style="text-align:left">
<ul>
<li>Cycles the tri-state checkbox among unchecked, mixed, and checked states.</li>
<li>When the tri-state checkbox is unchecked, all the controlled checkboxes
are unchecked.</li>
<li>When the tri-state checkbox is mixed, the controlled checkboxes return
to the last combination of states they had when the tri-state checkbox
was last mixed or to the default combination of states they had when the
page loaded.</li>
<li>When the tri-state checkbox is checked, all the controlled checkboxes
are checked.</li>
</ul>
</th>
</tr>
</thead>
<tbody></tbody>
</table>

Ember has a checkbox input helper- [https://guides.emberjs.com/release/templates/input-helpers/\#toc\_checkboxes](https://guides.emberjs.com/release/templates/input-helpers/#toc_checkboxes) - but it should not be used in the place of common sense. Use this helper if it is sensible to do so.

\(More explicit guidance and additional scenarios coming soon\)

## Part Three: Abstracting for reuse

Coming soon!

## References

* [https://www.w3.org/TR/wai-aria-practices-1.1/\#checkbox](https://www.w3.org/TR/wai-aria-practices-1.1/#checkbox)
* [https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox)
* [https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/checkbox\_role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/checkbox_role)
* [https://codepen.io/jensimmons/pen/KKPzxJa](https://codepen.io/jensimmons/pen/KKPzxJa)

<!-- {% hint style="info" %} -->
Feedback is welcome! Visit the [GitHub repository for this project](https://github.com/MelSumner/ember-component-patterns) to raise an issue.
<!-- {% endhint %} -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Styling: Checkboxes

## Preventing Text Selection

Occasionally, when a checkbox is activated or deactivated, the input's label text appears selected. If this is bothersome, a little CSS can help:

```css
label {
user-select: none;
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
description: >-
This page contains some general knowledge that may be helpful to review before
looking at the form component guides.
---

# Considering Forms

## Labels

All form inputs are required to have an associated `<label>` element. The `<label>` element should not be used in any other context.

To associate a form input and a label, the value of the `for` attribute in the &lt;label&gt; element should match the value of the input's `id` attribute. See this truncated example:

```markup
<label for="IDREF">Label Text</label>
<input id="IDREF" />
```

It should then be considered how to generate such a thing, since IDs must be unique. There are two approaches for Ember applications:

### UUID

One approach is to create a [`uuid`](https://en.wikipedia.org/wiki/Universally_unique_identifier) as a variable within the component, and increment the value as needed. If this approach is used, care must be taken to ensure that the values generated are valid values as per the HTML specification:

* _**must**_ begin with a letter \(\[A-Za-z\]\)
* may be followed by any number of letters, digits \(\[0-9\]\), hyphens \("-"\), underscores \("\_"\), colons \(":"\), and periods \("."\)

### GUIDFOR

Ember has [`guidFor`](https://api.emberjs.com/ember/release/functions/@ember%2Fobject%2Finternals/guidFor) which returns a unique ID for the object. If the object does not yet have a guid, one will be assigned to it, and it can be called on any object, including DOM element objects.

{% hint style="info" %}
While it is also valid to wrap the input in a `<label>` element and avoid using the `for` attribute entirely, this approach is not advised because it limits the designs that can be used. When considering how the code is written, it is essential to consider flexibility for styling as an integral part of the approach. Set your future up for success!
{% endhint %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Use of aria-live

The `aria-live` attribute should be used with _**extreme**_ caution, as it can provide an incredibly poor user experience for users with assistive technology.

Because the `aria-live` attribute can be used on any HTML element, and effects _all_ child elements of the element marked with the attribute, a thorough understanding of how this role works and when to use it is especially necessary for competent software engineers who build web-based solutions.

The `aria-live` attribute can have a few different values \(`POLITENESS_SETTING`\): `off`, `polite`, and `assertive`.

## Politeness Setting: Off

> Assistive technologies should not announce updates unless the assistive technology is currently focused on that region.

Regions which contain information that will receive updates but are not critical for users to know about immediately can be marked with `aria-live="off"`. Once that region has \(user-driven\) focus, the updates will be made known.

## Politeness Setting: Polite

> Assistive technologies should announce updates at the next graceful opportunity \(e.g., end of the current sentence\).

Regions that contain warning notifications that users _may_ need to know can be marked with `aria-live="polite"`.

## Politeness Setting: Assertive

> Assistive technologies should announce updates immediately.

Regions that contain urgent alerts or notifications \(such as error alerts\) that are imperative for users to know immediately should use `aria-live=assertive`.

## Pitfalls

The aria-live attribute should not be used for dynamic content that is non-critical. Of course, what constitutes critical should be driven only by determining if the content is important to the specific purpose of the page.

The purpose of the page should be clearly indicated by the `<title>` element in the page `<head>`. Another clue should be the `<h1>` of the page, which should also clearly indicate the purpose of that page.

It is a common practice to asynchronously load other page content like sidebar widgets or tabular data. In both of these instances, the areas should be marked with `aria-live=off` so they can indicate the updated information only once the user focuses on those elements.
36 changes: 36 additions & 0 deletions ember-handbook/app/ui-patterns/index/template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
description: >-
This guide intends to be aligned with the idioms and syntax of the Octane
edition of Ember.
---

# Ember Component Patterns

The goal of this guide is to cultivate a set of patterns that are practical and can reasonably be used by any Ember developer in their application. It is intended to be made publicly available with the [preview release of Ember Octane](https://emberjs.com/editions/octane/).

The component patterns here will, at least initially, be without CSS styling. This is to help clearly delineate form and function. "First, make it useful; then make it beautiful" as the saying goes. When necessary to demonstrate the validity of the approach, however, a sub-section on styling may be added to the pattern if it helps to demonstrate what might otherwise be thought of as impossible.

As this project matures, the [anti-patterns](https://en.wikipedia.org/wiki/Anti-pattern) will be explored by adding more prose and explains to demonstrate why other options were not chosen, providing both a well-lit path for success and a knowledge base for the shadows.

What one can obtain from this collection of patterns depends on the reader; however a few potential types of readers, and possible goals, have been kept in mind. Some examples:

## For Developers

* write more technically accurate code
* worry a little bit less about writing code that is not accessible
* have easy-to-reference base requirements for common component patterns
* have confidence in the code you produce

## For Designers

* understand what components really need to have from a functional perspective
* ensure that designs will include the necessary functionality and accessibility
* focus on design within clear technical constraints

## For BAs & TPMs

* reference to help you more accurately know base requirements for the new feature\(s\) you want to add to your project
* confidently plan out projects more accurately by reducing "unknown unknowns"

<!-- Hint thingy here -->
Feedback is welcome! Visit the [GitHub repository for this project](https://github.com/MelSumner/ember-component-patterns) to raise an issue.
4 changes: 4 additions & 0 deletions ember-handbook/app/ui-patterns/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.spacer {
display: block;
height: var(--s-1);
}
Loading