Set variable within condition - css

I need to set variable in LESS based on conditions.
#body-background-color: black;
#post-background-color: white;
#post-stacked-effect: true;
.content-padding() when ( #body-background-color = #post-background-color ) and not (#post-stacked-effect = true){
#content-padding: 0px;
}
.content-padding() when not (#body-background-color = #post-background-color), (#post-stacked-effect = true) {
#content-padding: 20px;
}
body {
.content-padding();
content: "#{post-stacked-effect}";
padding: #content-padding;
background-color: #body-background-color;
.post {
background-color: #post-background-color;
}
.some-math {
margin: -#content-padding -#content-padding #content-padding;
}
}
#content-padding is always 0px. Can you please help me fix that condition?

Its a bug in PHP Less Compiler. I have to do PHP workaround.
When I'm preparing variables for that compile i added function which calculates the padding in php and inject it into LESS.
Its only one row in PHP.
$lessvars["content-padding"] =
( $lessvars["body-background-color"] == $lessvars["post-background-color"] &&
!filter_var($lessvars["post-stacked-effect"], FILTER_VALIDATE_BOOLEAN) )
? "0px" : "20px";

You are creating a new scope with the 'when' guarded mixin which isn't visible outside.
So this isn't even supposed to work, and certainly isn't a bug.
You can instead create a function and call that from the 'when'.
#body-background-color: black;
#post-background-color: white;
#post-stacked-effect: true;
body {
.padding(#padding)
{
padding: #padding;
.some-math
{
margin: #padding + 2em;
}
}
& when ( #body-background-color = #post-background-color ) and not (#post-stacked-effect = true){
.padding(0px);
}
& when not (#body-background-color = #post-background-color), (#post-stacked-effect = true) {
.padding(20px);
}
}
I've removed the irrelevant css, and renamed it because stackoverflow formats hyphenated function names weirdly.

Related

If statements in SCSS using CSS variables

My JS code sets a variable on the page:
if (error) {
document.querySelector('body')?.style.setProperty('--pickererror', `'${ error }'`);
}
My SCSS code uses that variable for some content:
$error: var(--pickererror);
// ERROR MESSAGE
.picker-toolbar::before {
content: $error;
color: red;
padding: 5px 35px 0px 35px;
text-align: center;
}
.picker-toolbar {
#if str-length($error) > 0 {
order: 1 !important;
}
}
The ::before section works completely. However the #if clause executes even if there is no error, and the .picker-toolbar is always at order: 1.
I have checked that --pickererror is not present when there's no JS error. I've tried any number of permutations, such as
Putting the #if line before the .picker-toolbar line
Simply using #if $error
Using #if var(--pickererror) in place of the $error variable.
How do I make this work?
What if you remove the logic from your stylesheet entirely, and instead rely on the dynamic property value from your JavaScript?
For example:
body {
--pickererror: 0; // Default value for the "order" property
}
If your JavaScript detects an error, the --pickererror is given a value of 1 !important;
if (error) {
document.querySelector('body').style.setProperty('--pickererror', '1', 'important');
}
And in your stylesheet, you only need a single rule that changes the order if the JavaScript says so.
.picker-toolbar {
order: var(--pickererror, 0); // Defaults to "0" also if the variable doesn't exist
}
I realize this only makes sense if you are using --pickererror mostly as a boolean (I thought since you're checking if its length is greater than 0). If you're actually using the string value, it would be better to create an --error-order variable for this single purpose I guess.

Is possible to make an important property value from CSS selector a non important one?

Is possible to make an important property value from CSS selector a non important one?
For example: Bootstrap 3 defines .hide class as:
.hide { display: none !important; }
Is possible to remove the important value without modifying the BS3 source code?
Thinking at somethingl iike
.hide { display: none !remove-important; }
Note that I want to keep the same value! I don't want to set block !important to .hide class because that would not be correct...
I already added a new class .hide-non-important and used it where it was needed:
.hide-not-important { display: none; }
...but the question is: is there any alternative to this?
You don't need to edit the original source code. Just create a stylesheet and place it after the Bootstrap stylesheets and add this to it:
.hide { display: block !important; }
Now, having said this, I would be very careful about doing this. You don't know how many elements across your site have applied this class, and you will almost certainly get unanticipated results.
There is obviously a reason that this class has been applied, I would suggest either:
don't do this
add some other class to the element and add styles to that. Adjust your markup (or use js to apply the class if need be) to something like:
<div class="hide custom-hide-reset"></div>
Then add this style to the stylesheet you've created:
.custom-hide-reset { display: none; }
You can overwrite this with:
body .hide { display: none !important; }
.someclass.hide { display: none !important; }
those two examples have higher priority
The stylesheet objecs are accessible from javascript
var sheets = document.styleSheets
Once you have the stylesheet array, you can iterate over the rules
var rules = sheets[i].cssRules || sheets[i].rules // browser dependency
Each rule has a style property, which is mutable in the usual way.
rule.style[cssPropName] = value;
There is a method on the sheet to delete a rule by index, either deleteRule or removeRule, depending on the browser.
Bottom line, you can find the rule and either edit it or delete and re-add it in a modified form.
Reference: http://www.javascriptkit.com/dhtmltutors/externalcss3.shtml#.Ujsin4ZmjAs
Since the question can be splitted in
how can I remove !important from a rule applied to my page but
without changing its value and
without editing the original CSS file
For pure spirit of adventure only, I think it would be possible in the following way:
load the CSS with jQuery;
perform a replacement of its content, by searching .hide { display: none !important; } and replacing it with .hide { display: none; };
apply the new in-memory altered CSS to the page.
Taking for good the solution posted in this answer, it could be modified with something (totally untested, just to get the idea) like:
$.get(myStylesLocation, function(css)
{
var alteredCss = css.replace(".hide { display: none !important; }",".hide { display: none; }");
$('<style type="text/css"></style>')
.html(alteredCss)
.appendTo("head");
});
You can override the current !important value by another one like
.col{ color:red !important; }
.col{ color:green; } // wont work
.col{ color:blue !important; } // will work and set color blue instead of red
DEMO.
Update :
This question is not about JavaScript but as an alternative you can accomplish the task using these technique, remove the rule using JavaScript and then add a new rule again.
function getCSSRule(ruleName, deleteFlag) {
ruleName=ruleName.toLowerCase();
if (document.styleSheets) {
for (var i=0; i<document.styleSheets.length; i++) {
var styleSheet=document.styleSheets[i];
var ii=0;
var cssRule=false;
do {
if (styleSheet.cssRules) {
cssRule = styleSheet.cssRules[ii];
} else {
cssRule = styleSheet.rules[ii];
}
if (cssRule) {
if (cssRule.selectorText.toLowerCase()==ruleName) {
if (deleteFlag=='delete') {
if (styleSheet.cssRules) {
styleSheet.deleteRule(ii);
} else {
styleSheet.removeRule(ii);
}
return true;
} else {
return cssRule;
}
}
}
ii++;
} while (cssRule)
}
}
return false;
}
function killCSSRule(ruleName) {
return getCSSRule(ruleName,'delete');
}
function addCSSRule(ruleName, v) {
if (document.styleSheets) {
if (!getCSSRule(ruleName)) {
if (document.styleSheets[0].addRule) {
document.styleSheets[0].addRule(ruleName, v,0);
} else {
document.styleSheets[0].insertRule(ruleName+'{'+v+'}', 0);
}
}
}
return getCSSRule(ruleName);
}
// Check the rule before deleting
console.log(getCSSRule('.col')); // .col { color:red !important; }
// At first remove the current rule
killCSSRule('.col');
// Now assign nre rule
addCSSRule('.col', 'color: red');
// Check the rule after deleting
console.log(getCSSRule('.col')); // .col { color:red; }
DEMO. ( Source : Totally Pwn CSS with Javascript )

Less if string is not empty conditional logic

I'm using dotless to dynamically change the look of my site from an admin page.
Essentially I use regular expressions to read the variables defined in the less and then give the user the option to change the value of the variables.
I'm wanting to have the option to set a background image. Essentially I need a way to check if the string is empty if its not then add the background image mixin.
#BackgroundImage: '';
.showBackground(#fileName) when (#fileName != '') {
background-image: url('../Themes/images/backgrounds/#{fileName}');
}
body {
.showBackground(#BackgroundImage)
}
So the default is no background '' when the user sets a background the variable #BackgroundImage will be set to 'backgroundImage1.jpg'
How can I get this empty string logic to work?
P.S I've tried setting the variable to #000000 and using isstring() but it would appear to return true.
You want to use the when not instead of negating the condition.
LESS
.showBackground(#fileName) when not (#fileName = '') {
background-image: url('../Themes/images/backgrounds/#{fileName}');
}
Output
#BackgroundImage: ''; // No output.
#BackgroundImage: 'foo'; // background-image: url('../Themes/images/backgrounds/foo');
Here's another possible solution that sets defaults and checks for use cases when the variable is not used. The problem it attempts to solve is using text as an indicator in some cases and a background image in others.
This worked out well enough even if there is some duplication. Your mileage may vary.
.pointer( #ico ) when not ( #ico ) {
&:extend(.global-sprite-image);
.sprite-position(#ico);
.sprite-height(#ico);
.sprite-width(#ico);
content: '';
margin-left: 2px;
line-height: 1;
}
.pointer( #ico:'›') when ( default() ) {
content: #ico;
font-size: 130%;
font-weight: bold;
font-family: #font-title;
padding-left: 2px;
line-height: 1;
}

Defining Variable Variables using LESS CSS

Say I have three separate color schemes that are used on various pages in a site. Each color has a a light, medium and dark tint defined, and the color scheme is defined by a class in the body. Assume that the "red" color scheme is the default. Like this:
Color Definitions:
#red-lt: #121;
#red-md: #232;
#red-dk: #343;
#green-lt: #454;
#green-md: #565;
#green-dk: #676;
#blue-lt: #787;
#blue-md: #898;
#blue-dk: #909;
Basic Default Style Example
body { background-color: #red-dk;
#container { background-color: #red-md;
p { color: #red-dk; }
}
}
Different Color Scheme Style Example
body.green { background-color: #green-dk;
#container { background-color: #green-md;
p { color: #green-dk; }
}
}
I'd like to use variables so that I don't have to re-write all of the color variations for each scheme, so that I can just write something like this:
body.[color-var] { background-color: #[color-var]-dk;
#container { background-color: #[color-var]-md;
p { color: #[color-var]-dk; }
}
}
…but I can't quite wrap my head around how to accomplish that. Help…?
Use interpolation and escaping, parentheses in the selector and parametric mixins to get the desired effect:
Dynamic variables by interpolation: In a string, "#{variable}" is replaced with the value of the variable. They can also be nested: Given #{#{var}-foo} and #var: bar;, the result is "barfoo".
The resulting value is quoted. To remove these quotes, prefix ~.
Dynamic selectors by Selector interpolation: body.#{var} turns into body.bar.
Example:
#red-md: #232;
#red-dk: #343;
.setColor(#color) {
body.#{color} { background-color: ~"#{#{color}-dk}";
#container { background-color: ~"#{#{color}-md}";
p { color: ~"#{#{color}-md}"; }
}
}
}
.setColor(~"red"); // Escape to prevent "red" turning "#FF0000"
//.setColor(~"blue"); etc..
Turns into:
body.red {
background-color: #334433;
}
body.red #container {
background-color: #223322;
}
body.red #container p {
color: #223322;
}
Note: When the answer was originally written, selector interpolation did not exist. See the previous revision for the solution if you're working with an old LESS compiler (before LESS 1.3.1a). Support for the old method will be dropped in LESS 1.4.0.
If those values really follow a predictable format like that, seems like a perfect case for a parametric mixin:
Less:
#red: #232;
#green: #565;
#blue: #898;
.theme (#color) {
background-color: #color - #111;
#container {
background-color: #color;
p { color: #color + #111; }
}
}
body.red {
.theme(#red);
}
Compiled CSS:
body.red{background-color:#112211;}
body.red #container{background-color:#223322;}
body.red #container p{color:#334433;}
I know this question is pretty old, but for those that come to this post my answer maybe can help
I`m not really sure for what you want to use this, but one of my suggestion is based on #ScottS answer. On my real world, I need to create a web app, where it would show several brands and each brand have their own text color, background and so on... so I started to chase a way to accomplish this in LESS, what I could easily do on SASS and the result is below:
LESS
// Code from Seven Phase Max
// ............................................................
// .for
.for(#i, #n) {.-each(#i)}
.for(#n) when (isnumber(#n)) {.for(1, #n)}
.for(#i, #n) when not (#i = #n) {
.for((#i + (#n - #i) / abs(#n - #i)), #n);
}
// ............................................................
// .for-each
.for(#array) when (default()) {.for-impl_(length(#array))}
.for-impl_(#i) when (#i > 1) {.for-impl_((#i - 1))}
.for-impl_(#i) {.-each(extract(#array, #i))}
// Brands
#dodge : "dodge";
#ford : "ford";
#chev : "chev";
// Colors
#dodge-color : "#fff";
#ford-color : "#000";
#chev-color : "#ff0";
// Setting variables and escaping than
#brands: ~"dodge" ~"ford" ~"chev";
// Define our variable
.define(#var) {
#brand-color: '#{var}-color';
}
// Starting the mixin
.color() {
// Generating the loop to each brand
.for(#brands); .-each(#name) {
// After loop happens, it checks what brand is being called
.define(#name);
// When the brand is found, match the selector and color
.brand-#{name} & {
color: ##brand-color;
}
}
}
.carColor {
.color();
}
Te result will be:
CSS
.brand-dodge .carColor {
color: "#fff";
}
.brand-ford .carColor {
color: "#000";
}
.brand-chev .carColor {
color: "#ff0";
}
This is very tricky and I had to use several elements to get what I needed, first used a set of mixins provided by Seven Phase Max and you can find it here and than, the #ScottS answer was the piece that was missing fro my puzzle... hope this helps you and others that need to create a set of Variables to be part of another variable and create a more dynamic less file.
You can copy my entire code and test at http://lesstester.com/
Try this
#red-lt: #121;
#red-md: #232;
#red-dk: #343;
#green-lt: #454;
#green-md: #565;
#green-dk: #676;
#blue-lt: #787;
#blue-md: #898;
#blue-dk: #909;
#color: 'red-lt';
div{
background: ##color;
border: 1px solid lighten(##color,20%);
}
To my knowledge, variable variable names are not supported in LESS. You could however restructure your declarations in a more semantic manner:
/* declare palette */
#red-lt: #121;
#red-md: #232;
#red-dk: #343;
#green-lt: #454;
#green-md: #565;
#green-dk: #676;
#blue-lt: #787;
#blue-md: #898;
#blue-dk: #909;
/* declare variables based on palette colors */
#lt: #red-lt;
#md: #red-md;
#dk: #red-dk;
/* ...and only use them for main declarations */
body { background-color: #dk;
#container { background-color: #md;
p { color: #dk; }
}
}
This should let you switch between palettes quite painlessly by avoiding explicit color references.

Scroll issues with Script.aculo.us / CakePHP autocomplete result list

Using the Ajax helper for CakePHP (currently 1.2.3.8166) to provide an $ajax->autoComplete list of results, and giving a result list back as the rendered view, if you use the mouse (and even the mouse wheel) to scroll results, all is well. Using the arrow keys, on the other hand, has the nasty effect of awkwardly scrolling the view: if I press down, the select box and the whole page move to the bottom of the browser's view pane; pressing up has the opposite effect of moving it to the top.
Has anyone else noticed this behaviour, and thought of something? the resulting list is provided by, e.g., this code (this gets $people from the autoComplete() function in the controller):
<ul>
<?php foreach($people as $person): ?>
<li><?php echo $person['Person']['id']; ?></li>
<?php endforeach; ?>
</ul>
(Just an example, I actually show the id and name / surname / commercial name).
The CSS for the list is as follows:
div.auto_complete {
position: absolute;
width: 250px;
background-color: white;
border: 1px solid #888;
margin: 0px; padding: 0px;
}
div.auto_complete ul{
list-style: none;
margin: 0px;
}
I received the answer to this problem on the cake-php newsgroup (available on http://groups.google.com/group/cake-php ).
The poster pointed to this page with the solution, and I copy it here:
Open the controls.js file (should be in app/webroot/js)
Search for the markPrevious function and change it to:
markPrevious: function() {
if (this.index > 0) {
this.index--;
} else {
this.index = this.entryCount-1;
this.update.scrollTop = this.update.scrollHeight;
}
selection = this.getEntry(this.index);
selection_top = selection.offsetTop;
if (selection_top < this.update.scrollTop) {
this.update.scrollTop = this.update.scrollTop-
selection.offsetHeight;
}
},
Search the markNext function and change it to:
markNext: function() {
if(this.index < this.entryCount-1) {
this.index++;
} else {
this.index = 0;
this.update.scrollTop = 0;
}
selection = this.getEntry(this.index);
selection_bottom = selection.offsetTop+selection.offsetHeight;
if(selection_bottom > this.update.scrollTop+this.update.offsetHeight) {
this.update.scrollTop = this.update.scrollTop + selection.offsetHeight;
}
},
Search for the updateChoices function and change lines
this.stopIndicator();
this.index = 0;
to
this.stopIndicator();
this.update.scrollTop = 0;
this.index = 0;
Finally, try the behavior. If it doesn't work at first, try deleting the cache files in app/tmp/cache (or clear your favorite server-side cache), your browser cache, and try again. Clearing app/tmp/cache worked for me.

Resources