9

I'm trying to enable users setting things in my app like colors, fonts, etc, as part of a customized profile they can maintain, and would like to change the elements dynamically as they provide input so they can see the changes in real time.

I can do this by binding the value of the input component to an attribute and then using that attribute in an expression in the style of the element, but this is going to get messy the more variables there are.

I'd rather just have a class on the element, and have the expression in the class CSS file, but it seems you cannot use expressions in CSS, only in the app or component markup.

Is there any way to get expression logic in the CSS file?

Here's a very simple component that shows what I'm trying do. NOTE: The color picker on Chrome on Mac updates the attribute in real time, and therefore updates the sample text area in real time also. Chrome on Mac is what all my users use. The Windows Chrome picker doesn't seem to update in real time, so if you take a look at this sample app on Windows, it may not work in real time the way it is for me.

App (test_CssExpression_App):

<aura:application >
    <c:test_CssExpression/>
</aura:application>

Component (test_CssExpression.cmp):

<aura:component>
    <aura:attribute name="backgroundColor" type="String" default="#FFFFFF"/>
    <aura:attribute name="textColor"       type="String" default="#000000"/>

    <lightning:input type="color"
                     label="Background Color"
                     name="backgroundColor"
                     value="{!v.backgroundColor}"/><br/><br/>

    <lightning:input type="color"
                     label="Text Color"
                     name="textColor"
                     value="{!v.textColor}"/><br/><br/>

    <div class="testDiv"
         style="{! 'background-color: ' + v.backgroundColor + '; color: ' + v.textColor + ';'}">
        This is some sample text
    </div>

</aura:component>

Style (test_CssExpression.css):

.THIS.testDiv {
    width: 200px;
    height: 200px;
    border: 2px solid black;
}

Here's what I'd like to do in the component (no style on the element, just class):

<aura:component>
    <aura:attribute name="backgroundColor" type="String" default="#FFFFFF"/>
    <aura:attribute name="textColor"       type="String" default="#000000"/>

    <lightning:input type="color"
                     label="Background Color"
                     name="backgroundColor"
                     value="{!v.backgroundColor}"/><br/><br/>

    <lightning:input type="color"
                     label="Text Color"
                     name="textColor"
                     value="{!v.textColor}"/><br/><br/>

    <div class="testDiv"
        This is some sample text
    </div>

</aura:component>

Here's what I'd like to do in the style (expression logic to set the colors in real time) but it's rejecting the use of expression logic in the CSS file:

.THIS.testDiv {
    width: 200px;
    height: 200px;
    border: 2px solid black;
    background-color: {!v.backgroundColor};
    color: {!v.textColor};
}

Any ideas appreciated - thanks!

4

I haven't done much Lightning yet so I'm sure there are probably better ways of doing this.

I think the problem is that CSS is essentially a static file and as such, you won't be able to incorporate server side variables. However, I think you should be able to place this directly into your Component

<aura:component>
    <aura:attribute name="backgroundColor" type="String" default="#FFFFFF"/>
    <aura:attribute name="textColor"       type="String" default="#000000"/>

    <style>
        .testDiv {
            background-color: {!v.backgroundColor};
            color: {!v.textColor};
        }
    </style>
    <lightning:input type="color"
                     label="Background Color"
                     name="backgroundColor"
                     value="{!v.backgroundColor}"/><br/><br/>

    <lightning:input type="color"
                     label="Text Color"
                     name="textColor"
                     value="{!v.textColor}"/><br/><br/>

    <div class="testDiv"
        This is some sample text
    </div>

</aura:component>

Seems to work, but as I say there's probably a more suitable way to do it.

  • 1
    I believe the correct method would be to override the default renderer, but I feel that this solution is far easier to understand. – sfdcfox Nov 29 '16 at 17:43
  • Yes, sure there's a much better way but I'm a bit green on Lightning, good to know the real solution – Phil Hawthorn Nov 29 '16 at 18:30
2

Alternatively you could make use of CSS vars, by assigning the attribute value to a var in the component, the variable will be visible in the css of the lightning component.


Lightning Component

<aura:component>
   <aura:attribute name="backgroundColor" type="String" default="#FFFFFF"/>
   <aura:attribute name="textColor" type="String" default="#000000"/>
   <aura:html tag="style">
       :root {
           --backgroundColor: {!v.backgroundColor};
           --textColor: {!v.textColor};
       }
   </aura:html>
   <lightning:input class="double-margin-bottom" type="color"
                 label="Background Color"
                 name="backgroundColor"
                 value="{!v.backgroundColor}"/>
   <lightning:input class="double-margin-bottom" type="color"
                 label="Text Color"
                 name="textColor"
                 value="{!v.textColor}"/>
   <div class="testDiv">
       This is some sample text
   </div>
</aura:component>

CSS

.THIS.double-margin-bottom {
    margin-bottom: 2em;
}

.THIS.testDiv {
    width: 200px;
    height: 200px;
    border: 2px solid black;
    background-color: var(--backgroundColor);
    color: var(--textColor);
}

I have implemented this with some of the stuff I have done and it allows for updates to the styles using the attributes of the components, say if you modify the values in the controller for example. And just to mention this works fine in Winter '19 (Version 44.0)


EDIT: Also bear in mind this won't work in IE, but then not much does.

  • This isn't working if the component is repeated on the same page as the previous variable is overridden by the other component(s). I moved the CSS declarations into the style tag to make it work. – Vinod Rondla Dec 28 '18 at 22:32
  • Ah yes good point – Jonathon Chambers Jan 2 at 16:38
  • Just remembered that using the :root attaches the variables to the highest level element of the DOM. You can move the variables to a local scope, in this case #testDivId { --var1 : <whatever> } and that way you avoid conflicting styles using the component multiple times. – Jonathon Chambers Jan 7 at 11:03
1

Although Phil is right in that the CSS file is considered a static resource, it's not for this reason that attribute bindings aren't respected in component CSS, it's simply that Lightning doesn't do it by design, although technically it could.

The best practice to do what you're asking to do is to dynamically apply classes either in the component markup itself via an expression or in javascript.

<aura:attribute name="class" type="String" />

<div class="{! 'testDiv ' + v.class}">
  This is some sample text
</div>

In the controller:

{(
  init: function(cmp, event, helper) {
    cmp.set('v.class', "active");
  }
)}

In the CSS:

.active { 
  background-color: #FF0000;
}
  • May be I am missing something, How can you pick the user color selection and apply the color to the div with this approach? – Rao Nov 29 '16 at 18:05
  • 1
    Sorry I didn't see the exact use case, which is dealing with a user-provided color value. My answer applies more to the general use-case of dealing with styling in a lightning component although I think for what you're trying to do, what you have currently is unfortunately the cleanest way. I would strongly advise not to have style tags in your component's markup. – citizen conn Nov 29 '16 at 18:11
  • 2
    Perhaps you could provide some insight in your answer @citizenconn why it is not advisable to have <style> tags within the component markup, I'd certainly be keen to learn. Thanks – Phil Hawthorn Nov 29 '16 at 18:45
  • Thanks for the responses - I've been busy on some other items this week, but will give some things in this thread a try tomorrow and post results, and/or pick an answer. Thanks! – Florissant53 Dec 1 '16 at 1:47
1

You could do this either with inline style on elements or to dynamically add on classes to the elements you want. Then you could have a css file which has all the colours for each of them.

Inline Style

<div style="{!'color:'+v.primaryTextColour}"> text </div>

Dynamic classes

<div class="{!'text-color-'+v.primaryTextColour}"> text </div>

and

.text-color-000000 {
    color:#000000 
}
.background-color-000000 {
    color:#000000 
}
.text-color-00001c {
    color:#00001c 
} ...

etc.

I stress that this is a neither way is a GOOD idea but if you absolutely have to have this functionality, it's the only way I can think to get round the inablity to have dynamic CSS in lightning.

0

Starting Spring '18(Version 42.0 or later) <style> tags not allowed in components anymore.

You can’t add a <style> tag in component markup or when you dynamically create a component in JavaScript code. This restriction ensures better component encapsulation and prevents component styling interfering with the styling of another component. The <style> tag restriction applies to components with API version 42.0 or later.

Define component styles only in the style resource in a component bundle. All top-level elements in a component have a special THIS CSS class added to them. The THIS class effectively adds namespacing to CSS and prevents one component’s CSS from overriding another component’s styling.

One solution is mentioned above- to have all your styling in the CSS file of the component bundle, but the problem that I recently had with that approach was I was not able to access static resources, custom labels, and custom settings in the CSS file which I was able to use by keeping the CSS in the component (.cmp) file.

Example - below works if specified within component only

background-image: url("{! $resource.jpg }");

I actually changed the API version to 41.0 to make it work as there was no viable solution at my disposal, I was also doing CSS Media Queries. Other solution, I could think of (didn't try though) was to use expressions inside the component itself

<div style="background-image=url({! $resource.jpg })"></div>

P.S. I was reluctant to use it because of the media queries but will post another question this topic and with some try-outs to make the CSS dynamic for different client types.

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.