less / less.js

Less. The dynamic stylesheet language.
http://lesscss.org
Apache License 2.0
16.99k stars 3.41k forks source link

an idea to combine less function and css variable. Please help a little #3600

Closed arvinxx closed 3 years ago

arvinxx commented 3 years ago

As we know, less function can't work with css variables. There are lots of related Issues, like #3563 #3551 #3537 #3394 .

These issuses show we really want a way to combine less variable and css variable. Because less is a great tool to compile css in a programming way and css variable has the feature of dynamtic value and is a good way to switch theme.

It's true that is less can't use css variables in less function. But want we really want is to hook less variable and css variable.

At compile time, Less doesn't know anything about your HTML, let alone how a custom property is cascading down to a particular element, which is affected not only by the document tree, but inline styles, the cascade, and the sum total of all your stylesheets. In other words, the final value is only determined by the browser, and Less's compilation environment is, primarily, a Node.js one.

And thus, I come up a "theory" method to combine less function and css variable. Here it is.

First, declare less variable in less function way.

// palettes/index.less

@import "colorPalette"; // colorPalettes is a less funtion to generate color palette by one color

@blue-base: #1890ff;
@blue-1: color(~`colorPalette('@{blue-6}', 1) `);
@blue-2: color(~`colorPalette('@{blue-6}', 2) `);
@blue-3: color(~`colorPalette('@{blue-6}', 3) `);
@blue-4: color(~`colorPalette('@{blue-6}', 4) `);
@blue-5: color(~`colorPalette('@{blue-6}', 5) `);
@blue-6: @blue-base;
@blue-7: color(~`colorPalette('@{blue-6}', 7) `);
@blue-8: color(~`colorPalette('@{blue-6}', 8) `);
@blue-9: color(~`colorPalette('@{blue-6}', 9) `);
@blue-10: color(~`colorPalette('@{blue-6}', 10) `);

Secondly, declare css variables with less variable

@import "palettes/index";

@brand-color: #233ad2;

:root {
  @blue-base: @brand-color; //  here is a override of `@blue-base`

  --blue-1: @blue-1;
  --blue-2: @blue-2;
  --blue-3: @blue-3;
  --blue-4: @blue-4;
  --blue-5: @blue-5;
  --blue-6: @blue-6;
  --blue-7: @blue-7;
  --blue-8: @blue-8;
  --blue-9: @blue-9;
  --blue-10: @blue-10;
}

// here is a dark mode token
:root [theme^="dark"] {
  @import "palettes/dark"; // a dark mode file containing basic method to handle `@blue-*` variable with dark mode

  @dark-mode-primary-color: mix(@brand-color, @component-background, 85%);

  @blue-base: @dark-mode-primary-color;  //  dark mode override of `@blue-base`

  --blue-1: @blue-1;
  --blue-2: @blue-2;
  --blue-3: @blue-3;
  --blue-4: @blue-4;
  --blue-5: @blue-5;
  --blue-6: @blue-6;
  --blue-7: @blue-7;
  --blue-8: @blue-8;
  --blue-9: @blue-9;
  --blue-10: @blue-10;
}

by this method, we can get a generated css:

/* stylelint-disable no-duplicate-selectors */
/* stylelint-disable */
/* stylelint-disable declaration-bang-space-before,no-duplicate-selectors,string-no-newline */
:root {
  --blue-1: #f0f4ff;
  --blue-2: #cfdbff;
  --blue-3: #a1b5f7;
  --blue-4: #738beb;
  --blue-5: #4962de;
  --blue-6: #233ad2;
  --blue-7: #1524ab;
  --blue-8: #091285;
  --blue-9: #02055e;
  --blue-10: #010138;
}
:root [theme^="dark"] {
  --blue-1: #121321;
  --blue-2: #141733;
  --blue-3: #181e44;
  --blue-4: #1a235d;
  --blue-5: #1c297d;
  --blue-6: #1f2f9d;
  --blue-7: #3d50b1;
  --blue-8: #6377c6;
  --blue-9: #8d9ed5;
  --blue-10: #bbc5e4;
}

image

By this method , we can delcare a new less variable to hook less variable and css variable. and it will work geart with light mode and dark mode.

@primary-color: var(--blue-6);

.use-color {
  color: @primary-color;
}

The reason to we want to use less variable ohter than css variable is that css variable has too much syntax noise. It's much easier to write with a single @ than var(--.

However, It's not enough. Because the purpose is to hook @blue-1 with var(--blue-1).

And actually what want I just need is change the declare of @blue-*: less function to @blue-*: var(--blue-*) after executing less function.

the modifyVars param is used to replace value before less function executes, What I really need is a param to modify variable after executing less function. (There is a little scope of modify variable to avoid circular dependency,for example we don't want --blue-1: @blue-1 to --blue-1: var(--blue-1)).

So is there any way to achieve this? For example a plugin? I need more information to solve this problem, I think it will be a really meaning feature.

matthew-dean commented 3 years ago

This is really out of the scope of Github issues. A better place to ask this type of question ("Is there any way to...") is Stack Overflow.

matthew-dean commented 3 years ago

Closing to reduce issue noise. You're welcome to post a link to the related Stack Overflow question.

arvinxx commented 3 years ago

I post a question about it. https://stackoverflow.com/questions/65997822/an-idea-to-combine-less-function-and-css-variable-is-therr-any-way-to-handle-th

Acutally I don't think stackoverflow is the right place. Because the maintainer of this repo know the lifecircle of less's executing processing most.

What I want to know is just is there any way to modify less code after executing less function. If there is,it will help a lot. Otherwise,Ifx this can be a feature of it?

matthew-dean commented 3 years ago

@arvinxx I'm currently the primary maintainer. To be honest, I don't totally understand your question. But if it's essentially a feature request, Less is basically just in bug-fixing mode at this point.

rgnyldz commented 1 year ago

This could be nice. I like to use @somevar instead of long and a bit harder to write var(--somevar) in my less file

What we want is basically to print out the variable text instead of the actual value so we can access it from the dom. I for example want to let the visitor to change a "@primaryColor" value to a darker shade with a picker.

So in the less file we have ;

:root{
    --primary-color: #AF1763;
}

@primaryColor: var(--primary-color);

.somediv{
    color: @primaryColor;
}

this prints:

.somediv{
    color: #AF1763;
}

this not changing if I change the --primary-color: #AF1763; variable in dom. because less printed out the color hardcoded

So what would be nice is that it prints our like this;

.somediv{
    color: var(--primary-color);
}

This way if we change --primary-color: #AF1763; in root with JS or whatever the text color will be updated everywhere @primaryColor has been used in the less file.