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

Improve screen reader support #533

Closed
Turbo87 opened this issue Sep 2, 2017 · 15 comments
Closed

Improve screen reader support #533

Turbo87 opened this issue Sep 2, 2017 · 15 comments
Assignees

Comments

@Turbo87
Copy link
Collaborator

Turbo87 commented Sep 2, 2017

The Lighthouse report often shows the following warning:

<html> element does not have a [lang] attribute.

If an Ember app only has a single language then we can set that language in the index.html, but when supporting multiple languages it gets a little more tricky. I would like to propose doing something like this:

setLocale(locale) {
  // ...
  document.documentElement.lang = locale;
}

This should result in something like <html lang="de"> and will hopefully improve the Lighthouse score and screen reader support.

@jasonmit
Copy link
Member

jasonmit commented Sep 2, 2017

Hopefully can be done in way that would work with SimpleDOM's document (fastboot). Likely would need to go through document.documentElement.setAttribute('lang', locale).

@Turbo87
Copy link
Collaborator Author

Turbo87 commented Sep 2, 2017

that would be awesome, yeah

@Turbo87
Copy link
Collaborator Author

Turbo87 commented Oct 19, 2017

Looks like document is not available at all in fastboot. https://github.com/kimroen/ember-cli-document-title/blob/master/vendor/document-title/document-title.js#L91-L104 might help in finding an alternative solution though.

@jasonmit
Copy link
Member

Related: ember-fastboot/fastboot#180

@Turbo87
Copy link
Collaborator Author

Turbo87 commented May 28, 2018

oh, nice! I'll see if I can get that running here locally :)

@Turbo87
Copy link
Collaborator Author

Turbo87 commented May 28, 2018

// components/set-doc-lang.js

import Component from '@ember/component';
import { getOwner } from '@ember/application';

export default Component.extend({
  tagName: '',

  didReceiveAttrs() {
    this._super(...arguments);

    let dom = getDOM(this);
    if (dom) {
      let html = dom.documentElement;
      html.setAttribute('lang', this.lang);
    }
  },
});

// from https://github.com/yapplabs/ember-wormhole/blob/0.5.4/addon/utils/dom.js#L45-L63
//
// Private Ember API usage. Get the dom implementation used by the current
// renderer, be it native browser DOM or Fastboot SimpleDOM
export function getDOM(context) {
  let { renderer } = context;
  if (!renderer._dom) {
    // pre glimmer2
    let container = getOwner ? getOwner(context) : context.container;
    let documentService = container.lookup('service:-document');

    if (documentService) {
      return documentService;
    }

    renderer = container.lookup('renderer:-dom');
  }

  if (renderer._dom && renderer._dom.document) {
    // pre Ember 2.6
    return renderer._dom.document;
  }

  return null;
}

this seems to work quite well, but it has the disadvantage of needing a component 🤔

@Turbo87
Copy link
Collaborator Author

Turbo87 commented May 28, 2018

I managed to make it work with the intl service directly, but I had to slightly modify the getDOM() function:

// myapp/utils/getDOM.js

import { getOwner } from '@ember/application';

// adjusted from https://github.com/yapplabs/ember-wormhole/blob/0.5.4/addon/utils/dom.js#L45-L63
//
// Private Ember API usage. Get the dom implementation used by the current
// renderer, be it native browser DOM or Fastboot SimpleDOM
export default function getDOM(context) {
  let { renderer } = context;
  if (!renderer || !renderer._dom) {
    // pre glimmer2
    let container = getOwner ? getOwner(context) : context.container;
    let documentService = container.lookup('service:-document');

    if (documentService) {
      return documentService;
    }

    renderer = container.lookup('renderer:-dom');
  }

  if (renderer._dom && renderer._dom.document) {
    // pre Ember 2.6
    return renderer._dom.document;
  }

  return null;
}
// myapp/services/intl.js

import Service from 'ember-intl/services/intl';
import getDOM from 'myapp/utils/get-dom';

export default Service.extend({
  setLocale(locale) {
    this._super(...arguments);

    let dom = getDOM(this);
    if (dom) {
      let html = dom.documentElement;
      html.setAttribute('lang', locale);
    }
  },
});

This code works fine for me on Ember 3.1, but I have not tested it on any other Ember versions. In our case it goes through the documentService code path.

@jasonmit
Copy link
Member

jasonmit commented May 29, 2018

This code works fine for me on Ember 3.1, but I have not tested it on any other Ember versions. In our case it goes through the documentService code path.

I'm open to accepting extra features that may not work in older versions of Ember - just so long as it doesn't break older versions of Ember.

What are your thoughts on the language attr updating logic being contained within a Component (your original approach)?

I think it may catch people by surprise that the Service is updating the document directly. The downside is the user would need to be cognizant of dropping a component into their app template. The downside is also an upside in that if we're unable to support older versions of Ember the user can decide if they want to opted into it. The other upside is it will be easier for users to reason about what changed their html attribute.

@Turbo87
Copy link
Collaborator Author

Turbo87 commented May 29, 2018

What are your thoughts on the language attr updating logic being contained within a Component (your original approach)?

it would certainly have the advantage of being more explicit, but we're using Ember where everything should work right out of the box 😉

I'm not sure about this, but I think I prefer to do it right in the service if possible and possibly in the future provide an option to turn that behavior off if people complain about it. The only thing that is a little worrying is that the implementation uses private APIs and we should check with the core team if that is a good idea...

@jasonmit
Copy link
Member

jasonmit commented May 29, 2018

but we're using Ember where everything should work right out of the box

Fair. Lets move forward with it on the service.

The only thing that is a little worrying is that the implementation uses private APIs and we should check with the core team if that is a good idea...

👍 agreed

@Turbo87
Copy link
Collaborator Author

Turbo87 commented May 29, 2018

@rwjblue @ef4 any thoughts on ☝️?

@ef4
Copy link

ef4 commented May 29, 2018

Most of the time I favor doing anything with DOM in components, even if that means an addon needs to tell people to put a singleton component in their templates. Trying to manipulate DOM from a service or route tends to make things fragile, especially in tests.

This particular case is simple enough that if you do it carefully it’s probably ok.

Bigger picture, there’s not really a strong reason we couldn’t make the <html> and <body> tags be inside an app’s templates, instead of being special outside them. The obvious way to implement this feature is to give people a helper and let them say <html lang={{locale-name}}>

@stale
Copy link

stale bot commented Oct 19, 2018

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Oct 19, 2018
@buschtoens
Copy link
Member

@Turbo87 Do you have time to / want to submit the PR for your code? Otherwise I'll submit the PR in your name. 🙏

@Turbo87
Copy link
Collaborator Author

Turbo87 commented Oct 22, 2018

@buschtoens feel free, happy to review 🙏

buschtoens added a commit that referenced this issue Oct 23, 2018
Closes #533.

Based on the code provided by @Turbo87 in #533 (comment)
buschtoens added a commit that referenced this issue Oct 23, 2018
Closes #533.

Based on the code provided by @Turbo87 in #533 (comment)
jasonmit pushed a commit that referenced this issue Oct 24, 2018
Closes #533.

Based on the code provided by @Turbo87 in #533 (comment)
jasonmit pushed a commit that referenced this issue Oct 24, 2018
Closes #533.

Based on the code provided by @Turbo87 in #533 (comment)
buschtoens added a commit that referenced this issue Oct 24, 2018
* feat: set document `lang` attribute

Closes #533.

Based on the code provided by @Turbo87 in #533 (comment)

* feat: set document `lang` attribute

Closes #533.

Based on the code provided by @Turbo87 in #533 (comment)

* refactor: call updateDocumentLanguage explicitly

* chore: remove fix that belongs in extra PR

* fix: syntax error

* chore: fixing failing test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants