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

cdk-high-contrast mixin not working anymore without :host selector #18147

Open
biolauri opened this issue Jan 10, 2020 · 2 comments · May be fixed by #18152
Open

cdk-high-contrast mixin not working anymore without :host selector #18147

biolauri opened this issue Jan 10, 2020 · 2 comments · May be fixed by #18152
Assignees
Labels

Comments

@biolauri
Copy link

@biolauri biolauri commented Jan 10, 2020

Since reworking the cdk-high-contrast mixin in #17378 for Angular 9, this mixin doesn't work anymore when not being used within :host in ViewEncapsulation.Emulated (which is currently default).

As the new High Contrast Service adds its classes to the root of the document, the compound selector generated by the mixin relies on the class cdk-high-contrast-active (or any other derived target class) being outside of the ViewEncapsulation for the current component. But without using the :host selector around, every selector gets their component attribute for View Encapsulation.
Inside :host, it does work as Angular's preparing ViewEncapsulation is ignoring everything before :host.

This should at least be documented, but I would prefer a way of handling in all cases. I'd say, it's bad practice to wrap everything in :host, which is a workaround currently.

By the way, the example in the current documentation doesn't work either (and the documentation is quite outdated, for which I'll add another issue).

Reproduction

Steps to reproduce:

  1. Use cdk-high-contrast mixin outside of :host selector in Angular 9 Release Candidate
  2. See that its coding is not applied in high contrast mode
  3. Use mixin within :host selector
  4. See that its is applied in high contrast mode
StackBlitz
  1. Open StackBlitz to reproduce
  2. Click button to emulate high contrast mode
  3. Inspect applied SCSS coding (the red border should be seen instead of the green one)

Expected Behavior

The mixin should generate output, so that the attribute (component) selector doesn't get added to the .cdk-high-contrast-active selector:

.cdk-high-contrast-active   [_nghost-umf-c0]   .border-in-high-contrast[_ngcontent-umf-c0]{border:2px solid green}

In StackBlitz, the green border.

Actual Behavior

Attribute (component) selector ([_ngcontent-umf-c0]) does get added to the .cdk-high-contrast-active selector:

.cdk-high-contrast-active[_ngcontent-umf-c0]   .border-in-high-contrast[_ngcontent-umf-c0]{border:2px solid red}

In StackBlitz, the red border.

Environment

  • Angular: 9.0.0-rc.7
  • CDK/Material: 9.0.0-rc.7
  • Browser(s): Internet Explorer; Chrome with adding cdk-high-contrast-active class manually
  • Operating System (e.g. Windows, macOS, Ubuntu): Windows
@biolauri

This comment has been minimized.

Copy link
Author

@biolauri biolauri commented Jan 10, 2020

Two colleagues and me found a workaround, which should work for most Angular users: A mixin that uses the cdk-high-contrast mixin and wraps its output with a :host selector if the selector chain does not already contain one.
That will fix it for the most used use cases, but it doesn't cover everything:

  • If you've set ViewEncapsulation.None (either globally or for a specific component), the :host selector doesn't work at all, so adding it via this mixin will break your code (as it is then left unchanged by Angular and interpreted as the native ShadowDOM :host selector in supported browsers, but without the component's being converted to ShadowDOM)
  • You can't use this mixin outside of component styles as there's no :host context and no View Encapsulation; use the original one instead as it works perfectly fine here
  • Just for completeness sake: This will also break if you use the deprecated ::ng-deep around the workaround as you scope the whole selector chain to the host/component (which it wouldn't without the added :host selector; this will lead to unexpected, weird behaviour and you don't want to do this, but it's currently supported (but deprecated), so I just want to mention it

If there's not an easy solution to this issue right now, it might be worth thinking about this as an official workaround with some input to toggle the :host thingy and good documentation.
It then might also be better to rewrite the workaround mixin to use the :host() or :host-context() selector directly with the high contrast target class as an argument. We didn't want to as we wanted to provide only a wrapper.

And, finally, the code:

/// Solves the problem that the current cdk-high-contrast mixin is falsely applied with view encapsulation.
///
/// As workaround, the styling is wrapped in a :host which disables the view encapsulation for prior selectors and
/// therefore the view encapsulation for the added .cdk-high-contrast-* class. It also leads to one only being able to
/// style within the current component for high contrast mode (i.e. the view encapsulation cannot be disabled). Global
/// high contrast styling should not be in a *.component.scss file anyway.
///
/// @param {string} $target [active] - Directly forwarded to the cdk-high-contrast call
/// @content Styles to be applied when high-contrast is active
/// @throws Input type check (in called cdk-high-contrast), check that no host-context is part of '&' (would get broken; see https://github.com/angular/angular-cli/issues/16631)
@mixin app-high-contrast($target: active) {
  $app-selector-context: #{&};
  @if (string.index($app-selector-context, ':host-context')) {
    @error 'The mixin app-high-contrast does not work with :host-context, see https://github.com/angular/angular-cli/issues/16631';
  }
  // if the selector context already contains a :host, it already works and adding another one would break it
  @if (string.index($app-selector-context, ':host')) {
    @include cdk-high-contrast($target) {
      @content;
    }
  } @else {
    // apply styling with :host
    @at-root {
      :host {
        @if ($app-selector-context == '') {
          @include cdk-high-contrast($target) {
            @content;
          }
        } @else {
          #{$app-selector-context} {
            @include cdk-high-contrast($target) {
              @content;
            }
          }
        }
      }
    }
  }
}
@jelbourn

This comment has been minimized.

Copy link
Member

@jelbourn jelbourn commented Jan 11, 2020

I sent #18152 that I think should fix this; we definitely want the mixin to work emulated mode (and no encapsulation).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.