How to override mixins in LESS CSS 1.4+ - css

I've been using what I thought was a very elegant pattern for defining the styles of reusable components/widgets, using LESS. It works beautifully in LESS 1.3-, but after upgrading recently, my whole library is broken. Does anyone know a way to accomplish something like this in 1.4+?
Here's a very simple example of a component:
#componentName {
.loadMixins(){
.text() {}
.header() {}
}
.apply(){
> h3 {
// markup-specific styles
padding: 3px;
margin-bottom: 0;
// custom styles
.header();
}
> div.body, > div.popup p {
color: red;
// custom styles
.text()
}
}
}
And here's how it would be used:
.coolWidget {
#componentName.loadMixins();
// override mixins here
.text(){
color: green;
}
#componentName.apply();
}
This keeps all the markup-dependent styles abstracted from the user. I could completely change my markup and the user's styles would still work. According to the less.js changelog, 1.4.0 Beta 1 has a line "variables in mixins no longer 'leak' into their calling scope"
Is there any way around this?

Strictly speaking nested variables and mixins are still expanded into calling scope unless this scope already has those names defined.
Your example above results in a error:
SyntaxError: .header is undefined...
and it's expected as no .header() is actually defined within the .coolWidget (or anywhere else).
This can be fixed by providing "default" definitions for .text and .header somewhere inside #componentName.
For example if you modify .loadMixins() to:
.loadMixins() {
.text();
.header();
// default properties in case a caller does not provide its own:
.text() {}
.header() {}
}
then the example compiles OK and all text/header properties are overridden as expected.
I can imagine how your library may become broken because of new scope rules but this particular example you gave above does not illustrate the problem.

Related

How to think about styling AngularJS components?

I'm working on an AngularJS project with the aim of slowly getting things in order for Angular 6, or whatever version is out when we start on the upgrade. One of the big pieces of that work is converting existing directives into components.
The thing I'm struggling the most with, is that every instance of a component introduces an extra element into the DOM that wraps my actual component HTML and breaks the hierarchy, making it very hard to write CSS that does what it needs to.
To illustrate my dilemma, imagine a simple component called alert that provides styling for various types of messages you want a user to pay attention to. It accepts two bindings, a message and a type. Depending on the type we will add some special styling, and maybe display a different icon. All of the display logic should be encapsulated within the component, so the person using it just has to make sure they are passing the data correctly and it will work.
<alert message="someCtrl.someVal" type="someCtrl.someVal"></alert>
Option A: put styling on a <div> inside the extra element
Component template
<div
class="alert"
ng-class="{'alert--success': alert.type === 'success', 'alert--error': alert.type === 'error'}">
<div class="alert__message">{{alert.message}}</div>
<a class="alert__close" ng-click="alert.close()">
</div>
Sass
.alert {
& + & {
margin-top: 1rem; // this will be ignored
}
&--success {
background-color: green; // this will work
}
&--error {
background-color: red; // this will work
}
}
This works fine as long as the component is completely ignorant of everything around it, but the second you want to put it inside a flex-parent, or use a selector like "+", it breaks.
Option B: try to style the extra element directly
Component template
<div class="alert__message">{{alert.message}}</div>
<a class="alert__close" ng-click="alert.close()">
Sass
alert {
& + & {
margin-top: 1rem; // this will work now
}
.alert--success {
background-color: green; // nowhere to put this
}
.alert--error {
background-color: red; // nowhere to put this
}
}
Now I have the opposite problem, because I have nowhere to attach my modifier classes for the success and error states.
Am I missing something here? What's the best way to handle the presence of this additional element which sits above the scope of the component itself?
I personally do option A. This allows you to easily identify and create specific styles for your components without fear that they will overwrite site-wide styles. For instance, I'll use nested styles to accomplish this:
#componentContainer {
input[type=text] {
background-color: red;
}
}
This will allow you to make generic styles for your component that won't spill out into the rest of your solution.

LESS: mixin with non-class ruleset

In LESS, I am trying to define the button.c2 ruleset, to be applied to <button class="c2">...</button> elements. This ruleset is mainly based on the button.c1 ruleset. However, the following code produces a ParseError:
button.c2 {
button.c1;// Trying to import a ruleset
... // additional rules, such as font-size: 120%;
}
It seems to me that the ParseError is caused by the fact that the imported ruleset does not refer to a class or ID ("button.c1" does not start with a "." or a "#"). From the LESS documentation:
Any CSS class or id ruleset can be mixed-in that way.
Why is there such a limitation? Is there any way around it?
The limitation might just be ease of parsing, since . or # don't show up as the first character of a normal style rule the parser automatically knows that those should be mixed in.
You could get around it by defining .c1 as a mixin and using it for both buttons:
.c1() {
// c1 rules
}
button.c1 {
.c1;
}
button.c2 {
.c1;
// additional rules
}
However, coming up in LESS 1.4.0 are :extend selectors, which will allow you to do what you want. The syntax would be:
button.c2:extend(button.c1) {
// additional rules
}

Add !important to all styles for widget without javascript

I am building a widget that will be displayed on a client's site. We cannot use an iFrame so I am forced to use an exhaustive CSS reset (https://github.com/premasagar/cleanslate) to avoid interference with their styles. To use that solution, I need to add !important to all of my styles and because there are a lot of them and I want to keep this code easy to maintain, I'm looking for a more dynamic solution.
I am able to add !important to the stylesheet via javascript but that's not ideal for a production environment. I am using CodeKit and LESS and wondering if either of these are able to help me easily add !important to all styles when the CSS file is generated.
Mixin?
CodeKit config?
Update: Yes, LESS Can Help
I hate using !important except in the most extreme circumstances. Most people use it like a chainsaw when they should be using a scalpal to do the job. Nevertheless, I understand the issues facing widget developers like yourself, and your choice to use https://github.com/premasagar/cleanslate leaves you with no option.
Marc's answer noted a good feature of LESS, but he failed to demonstrate how that can help in this matter.
If you wrap your entire LESS code in a namespaced mixin, then this feature does exactly what is needed. So assume your widget code looked like this (you mentioned you are already using some type of class for your widget):
.yourWidgetClass div > p {
prop: something;
&:hover {
prop: some-hover-style;
}
}
.yourWidgetClass .someClass {
prop: something;
}
Then you can do this (wrapping all your widget code in #makeImportant() then calling that mixin with the !important feature noted in Marc's answer):
#makeImportant() {
.yourWidgetClass div > p {
prop: something;
&:hover {
prop: some-hover-style;
}
}
.yourWidgetClass .someClass {
prop: something;
}
}
& {
#makeImportant() !important;
}
This produces the following CSS Output:
.yourWidgetClass div > p {
prop: something !important;
}
.yourWidgetClass div > p:hover {
prop: some-hover-style !important;
}
.yourWidgetClass .someClass {
prop: something !important;
}
For my original (accepted) answer that was way more manually intensive, see the edit history.
I found that LESS can mark all properties set by a mixin at once as !important when specify !important after the mix-in call.
.someMixin() {
background-color: #fff;
cursor: pointer;
}
.someUsages {
.someMixin() !important;
}
Results in:
.someUsages {
background-color: #fff !important;
cursor: pointer !important;
}
For more information on this topic see the LESS doc about "The !important keyword".

Declare a global CSS property ? Is this possible?

I have a very wierd question, I dont know wether if its possible in css or not
Suppose I have say 3 different css classes as shown below, as you can see I have a common property of all these classes, I want to declare this color somewhere else and pass a reference to it here, so if next time I want to change the color I can simply change at one place rather than changing in all the 5 classes.
I know that you can use body{}, or a wrapper for this but that would affect the colors of the entire site right ? Is there a way to do this ?
Is this even possible ?
.abc {
color:red;
}
.abc2 {
color:red;
}
.abc3 {
color:red;
}
.abc4 {
color:red;
}
.abc5 {
color:red;
}
The bad news: you can't do it in CSS.
The good news: you can write in a meta-CSS language like LESS, which then processes a LESS file to pure CSS. This is called a "mixin".
In LESS:
#errorColor: red;
.error-color {
color: #errorColor;
}
#error-1 {
.error-color;
}
.all-errors {
.error-color;
}
More info: http://lesscss.org/#-mixins
if you want to declare all of them at a time, you can use:
.abc, .abc2, .abc3, .abc4, .abc5 {
color:red;
}
Or you can declare an additional class & add to all the .abc, .abc2.... & make its color:red;.
This can not be done with CSS, but that is still a very popular thing to do by using a CSS preprocessor such as LESS, SASS, SCSS, or Stylus.
A preprocessor will let you define a variable (say $red = #F00). It will replace the variable in your CSS document with the variable value for you, allowing you to write very DRY and module CSS.
This functionality is referred to as "CSS variables", which is part of the future spec, but not yet implemented on any browsers.
For now, the best way to do this in pure CSS is to declare an additional class for the desired "global", and then add that class to all relevant items.
.abc_global { color: red; }
.abc1 { /* additional styling */ }
.abc2 { /* additional styling */ }
<div class="abc1 abc_global"></div>
<div class="abc2 abc_global"></div>
With LESS
You are able to define that red color once:
.myRedColor {
color:red;
}
Now you can call that red on any CSS styles. Even NESTED styles! It's a wicked tool!
.abc1 {
.myRedColor;
}
.abc2 {
.myRedColor;
}
.abc3 {
.myRedColor;
}
.abc4 {
.myRedColor;
}
NESTED EXAMPLE:
.abc {
.itsEasyAsOneTwoThree{
.myRedColor;
}
}
Now all of our "itsEasyAsOneTwoThree" classes that are properly nested inside of an "abc" class will be assigned the red style. No more remembering those long #867530 color codes :) How cool is that?!
You can also use PostCSS with the plugin postcss-preset-env and support custom properties/variables, then use the :root selector to add global css variables.
:root {
--color-gray: #333333;
--color-white: #ffffff;
--color-black: #000000;
}

LESS issues (scope and explicit node name)

Is there any way to bypass LESS scoping? It's becoming annoying. Basically, I have a .text-box which defines background, border, etc. Then, in a sub-section there's a one-off change to add a margin-top: .text-box { margin-top: 10px }. Now I can't use .text-box within that section and get my original box styles; instead, all I get is the margin-top. How can I get the definition higher in the heirarchy? I suppose I could make it a function, and call that function in both places, but being that I'm using LESS, I want to do less and KISS. In PHP, you'd get to the global namespace by using / prefix, or in C++ using :: prefix.
Additionally, it doesn't seem like any definitions with the node name work for prototyping. Meaning, I can't declare it ul.products, and then use ul.categories { ul.products }. I have to omit the node name in order to re-use it. Meaning: .categories { .products }. Is this an oversight/impossibility?
Thanks
ok so let's say you've got your mixin defined, for example:
.text-box {
background: #eee;
border: 1px solid #ccc;
color: #333;
margin-top: 5px;
}
now you want to add property or modify it in some subsection, then simply do this:
div.content {
div.sub_section {
.text-box;
margin-top: 10px; // this will override 5px defined in the mixin.
}
}
...which is putting your mixin in place, and adding some property you need to add (which will override any property from the mixin itself BUT make sure the overriding property is defined AFTER the mixin is called.
it's not ideal solution, as it creates two declarations in the output css file (there will be one from mixin followed by the one you defined in .sub_section), but otherwise I don't know a solution to this problem other than defining a parametric mixin..
--
your second issue - I think that less doesn't support scope-limited definitions on purpose... if you really need to know that certain mixin is to be used by a specific tag, I would deal with it like so:
.ul_products { ... }
.ul_categories { .ul_products; ... }
ul.categories { .ul_categories; }
you can also define a bundle and call stuff from there:
#ul {
.products { ... }
.categories { ... }
}
ul.categories { #ul > categories; }
i hope i got it right.. ?

Resources