Search code examples
variableslessmixins

LessCss dynamic variables based on ancestor class


I have a page template which has a branding class on the body element:

<body class="brand-africa">
    <h1>Africa</h1>
</body>

Using the following Less, I can use a variable for the brand colour and apply it to the color of a CSS selector:

@brand-default: #649d84;
@brand-africa: #df6f20;
@brand-nz: #444;

.brand-color {
    .brand-default & {
        color: @brand-default;
    }
    .brand-africa & {
        color: @brand-africa;
    }
    .brand-nz & {
        color: @brand-nz;
    }
}

h1 {
    .brand-color;
}

This works well, but sometimes I want to apply the color to another CSS declaration - such as background-color, and to do this with the above code I'd need to duplicate the .brand-color mixin to instead apply background-color.

Ideally I'd like the mixin to return a variable - I know it's possible, but I can't work out how to use the classname to determine the returned value.


Solution

  • Well, no, you can't use class name to determine a variable or a return value. So it's usually done in reverse, for example like this:

    @brand-default: #649d84;
    @brand-africa:  #df6f20;
    @brand-nz:      #444444;
    
    h1 {
        .brand-colors();
    }
    
    h2 {
        .brand-colors(background-color);
    }
    
    .brand-colors(@property: color) {
        .color(default);
        .color(africa);
        .color(nz);
    
        .color(@name) {
            .brand-@{name} & {
                @value: 'brand-@{name}';
                @{property}: @@value;
            }
        }
    }
    

    Or like this:

    @brand-default: #649d84;
    @brand-africa:  #df6f20;
    @brand-nz:      #444444;
    
    h1 {
        .brand-colors({
            color: @color;
        });
    }
    
    h2 {
        .brand-colors({
            background-color: @color;
        });
    }
    
    .brand-colors(@style) {
        .brand-color(default);
        .brand-color(africa);
        .brand-color(nz);
    }
    
    .brand-color(@name) {
        .brand-@{name} & {
            @value: ~'brand-@{name}';
            @color: @@value;
            @style();
        }
    }
    

    Or even like this:

    .brand(default) {@{color}: #649d84}
    .brand(africa)  {@{color}: #df6f20}
    .brand(nz)      {@{color}: #444444}
    
    h1 {
        .brand-colors();
    }
    
    h2 {
        .brand-colors(background-color);
    }
    
    .brand-colors(@color: color) {
        .-(default);
        .-(africa);
        .-(nz);
    
        .-(@name) {
            .brand-@{name} & {
                .brand(@name);
            }
        }
    }
    

    Or something in between. Or... oh wait, there's whole family of methods for this stuff (incl. various combinations), see for example:

    Usually list/array/loop based methods are more compact, though personally I prefer something dumb like this:

    .themed({
    
        h1 {
            color: @color;
        }
    
        h2 {
            background-color: @color;
        }
    
    });
    
    .themed(@styles) {
        .-(default, #649d84);
        .-(africa,  #df6f20);
        .-(nz,      #444444);
    
        .-(@name, @color) {
            .brand-@{name} {
                @styles();
            }
        }
    }