26

Say we have a LWC with the following template in a component named example:

<template>
    <lightning-textarea class="test"></lightning-textarea>
</template>

And some css to go with it:

.test textarea {
    min-height: 500px;
}

The expected behavior is that the lightning-textarea will be 500px tall because the nested textarea has been made 500px tall.

The actual behavior is that nothing happens. The reason for this is that the compiled css will be:

.test[c-example_example] textarea[c-example_example] {
    min-height: 500px;
}

And the compiled (relevant parts of the) html will be:

<lightning-textarea c-example_example lightning-textarea_textarea class="test"> 
   <textarea lightning-textarea_textarea></textarea>
</lightning-textarea>

It's obvious that the CSS Module Scoping has scoped the textarea element to its parent, lightning-textarea and not our example component. This is an issue because the CSS processor has appended [c-example_example] not only to the .test part of the selector, but textarea as well.

It makes sense that CSS would be scoped downward, which is how Aura components handled scoping, but in LWC it's actually entirely isolated, so neither parent nor child elements can target it.

Is there any way around this? Or plans to support targeting child components? Being able to override and tweak the CSS of the standard component library was a very handy feature of Aura components, allowing customized appearances without having to rewrite the base component from scratch.

On top of that, this means that you can't reuse your own components in different contexts, giving them different styles in each. The component itself must be responsible for all of its styling. While this is possible it requires a lot of conditional class assignments which is frustrating.

  • 1
    This is a total nightmare. – Robs Jul 31 at 21:04
16

LWC is enforcing the shadow DOM style scoping, so you can't currently style other Elements outside your shadow tree.

That being said, there is the on-going ::part and ::theme proposal that would allow components to safely expose some of their internals outside their shadow tree to be customized. This feature will be shipped in Chrome 73 and the rest of the browser vendors are supportive. We are currently evaluating how to add this in LWC and in the lightning base components.

  • 1
    Welcome to SFSE! Always love having people on the Salesforce team come by. – Adrian Larson Jan 21 at 14:28
  • 1
    This is great news! I hope to hear more about this down the road – Benjamin Vogler Jan 23 at 0:04
  • 2
    It seems ridiculous that we have to resort to creating our own duplicates of standard base components if we want our own styling. Didn't have to do that in aura or visualforce. But LWC is better? – Peter Knolle Apr 26 at 19:18
  • 2
    @pmdartus I'm eager to know if you have any updates on ::part and ::theme implementation in LWC. – devforce May 8 at 5:38
  • 1
    @pmdartus any update on this yet? Would you recommend for now to just create our own components using Lighting Design System replicating Salesforce standard with addition styling options? e.g. my scenario is to just change the background colour of lightning-button and lightning-button-menu within a lightning-button-group. Also the standard variant 'inverse' styles of do lightning-button and lightning-button-menu don't seem to match too. – CloudPete Jul 5 at 9:19
16

We ran into the same problem. I think it is something that Salesforce should definitely address as it is the biggest limitation of LWC compared to Aura components that we have encountered so far.

For example, we wanted to give a different color to a lightning-icon component and the only way to do this is to change the css fill color of the svg element inside lightning-icon. But that svg is not accessible/visible to our component's css because of the strict CSS isolation enforced by LWC. Re-creating our own version of Salesforce base components or submitting an Idea anytime we want to do a little ui tweak to one of them does not seem viable to us.

The workaround that we ended up using is to put the CSS that we wanted applied to the inner shadow DOM of Salesforce Base Components in a static resource. Then we used the loadStyle method of lightning/platformResourceLoader module to load that css. The styles loaded this way do apply to the inner elements within Salesforce base components.

Documentation for the loadStyle method is available here: https://developer.salesforce.com/docs/component-library/documentation/lwc/create_third_party_library

Another approach would be to encapsulate your LWC component inside an Aura component and put the CSS targeting inner elements in the css file of that aura component. Aura components do not enforce strict CSS encapsulation so that CSS will apply.

Neither workaround is ideal so we are looking forward to a better solution provided by Salesforce. The ::part and ::theme proposal seems promising but it looks like it won't be available for a while.

  • The LoadStyle workaround is very nice. Alternatively, I was also able to hard-code the slds code, but that takes away the brevity of the lightning tags. I think it's very reasonable to have a boilerplate folder for all the inner-shadow-css available to each html template. Later, when they make those parts available, you can just migrate the external css to the internal. Also, I agree that the shadow root blockage is the biggest shortcoming of LWC compared to Aura, but this workaround takes that away. – DaveS Apr 21 at 16:13
  • I find it quite interesting that CSS loaded through the loadStyle does allow overriding the inner elements while the CSS coming from the .CSS of the lightning bundle doesn't allow this... Working together with a designer, who doesn't yet think in the context of component isolation and specificities from LWC, I must say I find the loading of the styles through a static resource (like you would usually do with traditional VF pages) seems to work best at this time. – KoenVM Sep 26 at 12:35
6

No, CSS isolation is intentional. The code you see now in the browser will eventually just render as Shadow DOM, which are essentially black boxes from the outside. As such, you'll need to resort to building your own custom components so you have better control over them.

I do think it's a bit silly that you can't explicitly set the height for a textarea, though. You might want to propose an Idea for that. Something about being able to have a supported method for inserting extra styles for standard components.

I do know that LWC is still being worked out, so we don't know what the final result will look like for some time yet. It'd be a lot less of a wait, though, if you just wrote your own by borrowing from SLDS and adding extra support for that.

  • Any idea how community themes will be applied to lwcs, given their encapsulation? – Caspar Harmer Jun 20 at 22:57
5

The workaround that I have been using is to create a <style> tag and add it to the dom. This is somewhat dangerous if not done correctly because it can affect the entire css of the page including standard Salesforce UI

export default class Example extends LightningElement {
  renderedCallback() {
    if (this.hasRendered) return;
    this.hasRendered = true;

    const style = document.createElement('style');
    style.innerText = `
      c-example lightning-textarea textarea {
        min-height: 500px;
      }
    `;
    this.template.querySelector('.some.element').appendChild(style);
  }
}

Note that the selector uses c-example to scope the css, but doesn't necessarilly have to do so.

  • 2
    this worked perfectly for m – aaron Jul 29 at 19:13
1

I wanted to chime in on this as I had the same issue. I worked around it by using the standard <textarea></textarea> and grabbing the value via this.temlate.queryLocator('textarea').value.

HTML

  <h1>
    Enter a CSV
  </h1>
  <textarea placeholder="Example: 9999999999,8888888888,7777777777,...">
  </textarea>

CSS

:host textarea{
  width: 100%;
  min-height: 300px;
}

:host h1{
  font-size: 18pt;
  color: black;
  font-style: bold;
}

JS

handleSubmit(){
    var rawText = this.template.querySelector('textarea').value;
    console.log(rawText);
}

I only have one textarea on my form, but you can be much more specific if needed. I started here:

https://developer.salesforce.com/docs/component-library/documentation/lwc/create_components_dom_work

I havn't tried it but I suspect this might also be useful: https://www.w3schools.com/jsref/met_document_queryselector.asp

-1

You can try using js something like this.

var btn= this.template.querySelector("lightning-button").shadowRoot.querySelector("button").setAttribute('style', 'background:red');
  • 1
    You need to add explanation to your answer. This will ensure that people understand what you did and why. – Eduard May 28 at 9:35
  • This doesn't work – zaitsman Jun 14 at 0:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.