Less CSS: & reverse parent with multiple classes? - less

This is the first time I'm finding the & parent selector extremely useful so that I don't need to redefine parents simply to modify a child element.
Of course this is actually easy with LESS, but I have to ask!
<div id="skrollr-body" class="nonav">
<div class="skrollable">
</div>
</div>
#skroller-body {
.skrollable {
margin-top:40px;
.nonav & {
// this parent selector lets me modify
// .skrollable without duplicating it
margin-top:0px;
}
}
}
The output of .nonav & is
.nonav #skroller-body .skrollable
I'm wondering if I can get #skroller-body.nonav .skrollable instead somehow without extra HTML markup (a wrapper div.nonav)?
Currently I'll just duplicate the parent
#skrollr-body {
margin-top:40px;
left:0;
width:100%;
.skrollable {
margin-top:40px;
position:fixed;
z-index:100;
.skrollable {
position:absolute;
.skrollable {
position:static;
}
}
}
&.nonav {
.skrollable {
margin-top:0px;
}
}
}
Or to save redundant output;
#skroller-body.nonav .scrollable { margin-top:0px; }
But the whole point here is CSS code that's easy to maintain and read.

The docs tell us:
The & operator represents the parent selectors of a nested rule and
is most commonly used when applying a modifying class or pseudo-class
to an existing selector
So:
#skroller-body {
&.nonav {
.skrollable {
// stuff
}
}
}
}

Related

Using BEM in LESS like SASS using parents

I am trying to refrence the base class to change a sub class based on a BEM modifier.
This works well in SASS, but I'm working with LESS now.
Heres the SASS refrence...
https://css-tricks.com/the-sass-ampersand/
And heres the code that dosn't work...
.s-body {
#self: &;
...
&__door {
...
&--state-past {
#{#self}__image {
...
}
}
}
&__image {
...
}
}
It isn't possible in LESS to assign the current class to a variable using an ampersand (&). But you can set the class name as a variable like this:
.s-body {
#self: .s-body;
}
After that, you can use the variable as a selector like this: #{self}. For further explanation, check the LESS documentation: http://lesscss.org/features/#variables-feature-variable-interpolation
In your example, this will result in the following code:
.s-body {
#self: .s-body;
&__door {
&--state-past {
#{self}__image {
}
}
}
&__image {
}
}

Build HTML by iteration with kotlinx.html

I'm trying to build an HTML list using kotlinx.html by iterating over a collection, for each element in the collection I want to build an LI tag inside a UL tag.
This is what I'm trying:
fun showProducts(products: List<Product>) {
document.getElementById(content.id)
?.append {
ol {
products.forEach {
this.li {
+it.name
}
}
}
}
}
But I get an error in browser console:
Uncaught (in promise) TypeError: closure$products.iterator is not a function
How can I iterate over the collection and add LI tags inside the UL tag for each product passed to the function?
maybe, instead of to use products.forEach, you can use a simple for (p in products). So the code will be:
fun showProducts(products: List<Product>) {
document.getElementById(content.id)
?.append {
ol {
for (p in products) {
li {
+p.name
}
}
}
}
}
see this link:
https://try.kotlinlang.org/#/Kotlin%20Koans/Builders/Html%20builders/Task.kt

How can I change css directly(without variable) in Blazor?

I am using the server-side of Blazor.
I want to change the CSS of the body.
In Jquery I can write the code like this easily:
$("body").css("overflow-y","hidden");
However, with this tutorial(Blazor Change Validation default css class names) said, it seems I can only change the CSS by changing the class name.
It is so complex while crossing the component, especially the body is at the top of all the components.
I wonder whether there is a way can changes CSS directly in Blazor. Thank you.
There are several ways of getting out of the "blazor way" of doing things and accomplishing css modification of an element.
Simplest: Just like you can use the class attribute, use the style attribute
<element style=#myStyle></element>
#code {
string myStyle;
void MyMethod() {
myStyle="overflow-y: hidden;"
}
}
Advanced: Use JS interop
a. In the main view (index.html or Pages/_Host.cshtml depending on project type), create a js endpoint for your component
<script>
window.applyStyleForElement = function(styleOp) {
document.getElementById(styleOp.id).style[styleOp.attrib] = styleOp.value;
}
</script>
b. In razor file:
#Inject IJRRuntime JSRuntime
<element id=#myId></element>
#code {
string myId = Guid.NewGuid().ToString("n");
async Task MyMethod() {
await JSRuntime.InvokeAsync("applyStyleForElement",
new { id = myId, attrib = "overflowY", value = "hidden" });
}
}
Finally, applying to your special case with body element ("advanced" method above).
a. In the main view (index.html or Pages/_Host.cshtml depending on project type), create a js endpoint
<script>
window.applyStyleForBody = function(style) {
document.body.style[style.attrib] = style.value;
}
</script>
b. In razor file:
#Inject IJRRuntime JSRuntime
(...)
#code {
async Task MyMethod() {
await JSRuntime.InvokeAsync("applyStyleForBody",
new { attrib = "overflowY", value = "hidden" });
}
}
Well, Blazor does not support direct css modification yet, since Web Assembly doesn't. Anyway heads up, it is on the road-map for Web Assembly/Blazor.
Therefor your best bet is, changing the class name with variables. At least for now.
I'M NOT SURE IT'S THE RECOMMENDED WAY BUT it works!
For one of my project, I include a in the page html itself, using params :
<style>
html
{
background-color:#_ColorCss;
}
</style>
//html stuff here
#code
{
public string Color{ get; set; } = "white";
string _ColorCss => $"{Color}"; //use this in case of formatting (ex : add 'px' or that kind of things)
//code stuff here
}
not the very sexiest way but it works
have fun !
Well, actually there is a way to do that and it works really good (it might suffer a little delay though).
I know this answer is a little bit late but it might help other people who face the same challenge.
We need to create some JS code that includes the wanted files:
function includeLeftStyle() {
appendStyle("left.css");
}
function includeRightStyle() {
appendStyle("right.css");
}
function appendStyle(path) {
var element = document.createElement("link");
element.setAttribute("rel", "stylesheet");
element.setAttribute("type", "text/css");
element.setAttribute("href", path);
document.getElementsByTagName("head")[0].appendChild(element);
}
The wished CSS can be called according to the language (any other coditions) in the MainLayout:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (// is left)
{
await JSRuntime.InvokeAsync<object>("includeLeftStyle");
}
else
{
await JSRuntime.InvokeAsync<object>("includeRightStyle");
}
}
}
Happy coding! :)
Beginnig from the #Tewr answer, we can also change the whole class:
a) In the main view (index.html or Pages/_Host.cshtml depending on project type), create a js endpoint
<script>
window.applyStyleForElement = function (styleOp) {
if (styleOp != null) {
document.getElementById(styleOp.id).className = styleOp.value;
}
}
</script>
b) Then in the razor file
async Task MyMethod(string sortColumn)
{
await JsRuntime.InvokeVoidAsync("applyStyleForElement",
new { id = sortColumn, value = "newClassName" });
}

SCSS color variables that belong to an object

I am refactoring a project's SCSS to map colors to their own variable in a _colors.scss file.
More often than not, I am encountering the situation in which a selector has a background-color and color. So, I end up writing the following variables:
$cool-table-bg: $brand-primary;
$cool-table-text: $white;
Pretty soon I end up with many, many versions of the above.
What I want to know is, can I assign the above to an object like you would do in JavaScript? For example:
$cool-table: {
bg: $brand-primary;
text: $white;
}
I'd like to be able to refer to such colors as $cool-table.bg.
Is this possible?
As Dan Gamble suggested, I’d definitely go for SASS maps. Write yourself some tools for color handling. Something like:
// define globally somewhere in your setup
$COLORS = ();
// use mixin to define colors groups by e.g. element name
// example: #include color-set(cool-table, bg, #ff0000);
// or pass in a map to define multiple colors at once
// example: #include color-set(cool-table, ( bg: #ff0000, border: #00ff00));
#mixin color-set($group-name, $color-name, $color-value: false) {
#if (type-of($color-name) == map) {
#each $name, $value in $color-name {
#include define-color($group-name, $name, $value);
}
}
#else {
#if(map-has-key($COLORS, $group-name)) {
$group: map-get($COLORS, $group-name);
}
#else {
$group: ();
}
$group: map-merge($group, ( $color-name: $color-value ));
$COLORS: map-merge($COLORS, ( $group-name: $group )) !global;
}
}
// access color values anywhere with this function by passing in
// element name and color identifier
// example: $bg-color: color-get(cool-table, bg);
#function color-get($group-name, $color-name) {
#if (map-has-key($COLORS, $group-name)) {
$group: map-get($COLORS, $group-name);
#if (map-has-key($group, $color-name)) {
#return map-get($group, $color-name);
}
}
#return false;
}
You can use maps. A good write up is here:
https://www.sitepoint.com/using-sass-maps/
To see the available functions you can go here and scroll down to map functions

Aurelia Dynamically Bound Value Converter

I'm running into an issue with Aurelia and am assuming that there is something I am missing.
I'm trying to create a 'generic' grid. I have removed a lot of the html to keep the example short, but the basic idea is this:
<template>
<require from="../value-converters"></require>
<table show.bind="rows.length">
<thead>
<tr>
<th repeat.for="columnDefinition of columnDefinitions">
${columnDefinition.displayName}
</th>
</tr>
</thead>
<tbody>
<tr repeat.for="row of rows">
<td repeat.for="columnDefinition of columnDefinitions">
<span if.bind="columnDefinition.isCurrency">${row[columnDefinition.propertyName] | numeralFormatter}</span>
<span if.bind="columnDefinition.isDate">${row[columnDefinition.propertyName] | dateFormatter}</span>
<span if.bind="!columnDefinition.isCurrency && !columnDefinition.isDate &&">${row[columnDefinition.propertyName]}</span>
</td>
</tr>
</tbody>
</table>
</template>
I want to be able to use the ValueConverters to help properly display certain types of column data. The above is currently working, but I want to have more value converters for other columns and the conditions will get unwieldy. My experience with Aurelia so far is that it offers fairly elegant solutions, but I have been unable to figure this one out as of yet.
I tried adding another property to the columnDefinition class like this formatter:string = undefined and then tried to create the spans like the following:
<span if.bind="columnDefinition.formatter">${row[columnDefinition.propertyName] | columnDefinition.formatter}</span>
<span if.bind="!columnDefinition.formatter">${row[columnDefinition.propertyName]}</span>
but the parser threw an error on the '.'.
Is there any way to achieve this? What is the 'aurelia-way' of dealing with this type of a problem.
Thanks in advance for any help that could be offered.
I ended up taking a similar approach to the one suggested by #Slyvain with a bit of a different twist:
import {DateValueConverter} from './date';
import {NumberValueConverter} from './number';
import {autoinject} from 'aurelia-framework';
#autoinject()
export class MetaValueConverter {
constructor(private date: DateValueConverter,
private number: NumberValueConverter) {
}
public toView(value, valueConverter, format) {
/* JUSTIFICATION: https://stackoverflow.com/questions/38898440/aurelia-dynamically-bound-value-converter#comment-65199423 */
/* tslint:disable:no-string-literal */
if (this[valueConverter] && this[valueConverter].toView) {
return this[valueConverter].toView(value, format);
} else {
return value;
}
}
public fromView(val, valueConverter, format) {
if (this[valueConverter] && this[valueConverter].fromView) {
return this[valueConverter].fromView(value, format);
} else {
return value;
}
}
}
Original code can be found here.
Hope this helps.
I followed #peinearydevelopment and went one step further again to create a fully dynamic value converter.
Usage is as follows ${myValue | dynamic:converterKey:converterArgs} or simply ${myValue | dynamic:converterKey} if no additional arguments are required. The converterKey is used to request a value converter that should be registered with the container. converterArgs is the array of arguments that you'd pass to the toView & fromView functions.
import { autoinject, Container } from 'aurelia-dependency-injection';
export type ValueConverterKey = new (...args: any[]) => object;
type ValueConverterFunc = (...args: any[]) => any;
interface ValueConverter {
toView?: ValueConverterFunc;
fromView?: ValueConverterFunc;
}
#autoinject()
export class DynamicValueConverter {
constructor(
private container: Container,
) { }
public toView(value: any, converterKey?: ValueConverterKey, ...converterArgs: any[]) {
if (!converterKey) {
return value;
}
return this.convertValueIfPossible(value, converterKey, converterArgs, 'toView');
}
public fromView(value: any, converterKey?: ValueConverterKey, ...converterArgs: any[]) {
if (!converterKey) {
return value;
}
return this.convertValueIfPossible(value, converterKey, converterArgs, 'fromView');
}
private convertValueIfPossible(value: any, converterKey: ValueConverterKey, converterArgs: any[], func: keyof ValueConverter) {
let converter = this.container.get(converterKey);
if (converter) {
let converterFunc = converter[func];
if (converterFunc) {
return converterFunc.call(converter, value, ...converterArgs);
}
}
return value;
}
}
Have you considered using a single <span> with a single general purpose converter that takes the column definition as a parameter and that delegates to the right converter? I think that would make the component markup simpler.
<span>${row[columnDefinition.propertyName] | formatCell:columnDefinition}</span>
And inside the formatter:
export class FormatCell {
toView(value, columnDefinition){
if(columnDefinition.isCurrency)
return new CurrencyConverter().toView(value);
if(columnDefinition.isDate)
return new DateConverter().toView(value);
return value;
}
}