less / less/less-meta

[RFC] Less v4 & v5 Roadmap (WIP)

Open
#39 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
No language data
Stars
5
Forks
0
PR merge metrics
No merged PRs in 30d

Description

This supplants this issue.

TODO - Does this repo need to exist? If it's just for meta-discussions, I think it could move into the DIscussions tab on the Less repo.

Less 4.x Roadmap

(Prep for Less 5.x)

Add deprecation warnings when parsing / evaluating

Changes
  • Add support for :extend(.foo !all) vs :extend(.foo all)
    • Added in #4319 but tests needed
  • Add support for --custom: @{variable}; (see below)
    • Added in #4319 but tests needed
  • Add selector() function as special parsing in a (variable) declaration value or function argument
    • Deprecate plain "anything goes" permissive parsing fallback for regular values
  • Add custom() function as special parsing of a custom declaration's value -- I'm not sure this is necessary, because ~"" exists? The reason I think I added this is because it checks that you have matching braces... Therefore it would guarantee, during parsing, that the parsed value is a valid custom declaration value.
  • There are a number of examples in Less tests of invalid CSS for input CSS. There is no contractual guarantee that Less support invalid CSS (outside of known legacy hacks), so invalid CSS should be removed from .less tests, such that a more accurate spec-compliant parser doesn't bork on those tests
  • Add @from syntax (@plugin to be deprecated)

Add a deprecation warnings for the following things. Note that warnings will be suppress-able with an additional option.

WARN when

  • calling a mixin without parentheses e.g. .mixin;
  • when encountering unknown parsing in a variable declaration value (when falling back to permissive parsing)
  • using ./ (forced division) operator. It's really not necessary and requiring parentheses around division is fine.
  • putting whitespace between mixin name and parentheses in a mixin call
  • leaking (unlocking) variables and mixins into parent scope. There are accessors and other chaining features to be introduced in Less 5.x - Note: this is tricky to catch. It requires leaking the mixin/variable first, but only throwing the deprecation warning if we detect that the leaked var / mixin is actually referenced in the parent.
  • leaking parent scope into caller scope. (Mixin calls have access to parent scope. This is unnecessary as you can just pass what you want into the mixin, and it also makes a mixin definition harder to reason about.) Similar to above, we should only throw the deprecation warning if we detect that the parent scope is referenced.
  • using :extend(.foo all) instead of extend(.foo !all)
  • using --custom: @variable <-- this is ambiguous and problematic, the only supported form should be: --custom: @{variable};
  • using @plugin (requires @from as a feature)
  • using Math.ALWAYS
  • using strictUnits: false
  • putting @charset at any place other than the start of a .css or .less file, or using a variable in a @charset

ERROR ?

  • Compiling a remote Less file should automatically disable @plugin and any other JavaScript evaluation - see this advisory. (Unless we figure out a way to isolate JS execution -- maybe running Deno.)

Less 5.x Roadmap

TODO - break out as separate, linked READMEs?

Changes
  • Replace Less parsing / evaluation engine with Jess (a language-agnostic stylesheet engine).
  • Replacing engine would bring a number of new features, including expression #{} syntax, better performance, and better chaining.
  • Discontinue support of Less plugins in favor of Jess plugins? (It's not clear there can be 100% compatibility since Less plugins are Less AST-specific. The only possible way to do this would be to do a lot of work to expose Jess AST nodes as Less AST nodes to a Less plugin, and Less AST nodes are very messy / inconsistent, so my preference would be migration documentation).
New
1. @from as replacement for @plugin - drop-in JS/TS modules

Uses native dynamic imports vs eval'd Function calls.

@from './values.ts' import (width);
2. Namespacing for Less functions

No more inherent conflict between Less functions and CSS functions. The author can signal exactly what they mean.

@from '#less' import (rgb);

.box {
  color: rgb(fade(@my-color, 10%));
}

...or:

@from '#less' import * as less;

.box {
  color: less.rgb(fade(@my-color, 10%));
  background-color: rgb(var(--some-runtime-var));
}
3. @use as replacement for @import

Polluting the @import at-rule has had lots of unforeseen consequences, so continued use of it would be deprecated. Unlike @import, @use will not leak into child imports, making it easier for IDE features like auto-complete when opening a child import.

Old:

@import './variables.less';
@import './somefile.less';  // variables.less is leaked into somefile.less
@use './variables.less';
@use './somefile.less'; // no leakage ("somefile.less" must also call `@use './variables.less'`)

.box {
  color: @some-var; // (from variables.less)
}
4. @include as alternative replacement for @import

Will only render qualified rules, without importing variables or mixins.

5. @reference as replacement for @import (reference)

Will only include variables and mixins. Like @use, will not leak variables into child imports / references. Will also be able to be used repeatedly without an explicit (multiple) flag.

6. Replacements for @import (css) './myfile.less' and @import (less) './myfile.css' features
@include (css) './myfile.less';
@include (less) './myfile.css';
// can also write:
@use (less) './myfile.css';
7. Unlike Sass, Less will support native CSS Nesting:
.box {
  &.foo { color: blue; }
}

...evaluates to:

.box {
  &.foo { color: blue; }
}

whereas:

.box {
  // note the hyphen
  &-1 { color: blue; }
}

...evaluates to:

.box-1 { color: blue; }

Note, however, that &-1 will be deprecated and will trigger a warning. The preferred form for Less moving forward for "merging" selectors will be a function-like &()

e.g.

.box {
  &(-1) { color: blue; }
}

i.e. you can also force a merge by doing:

.foo {
  &().bar { color: blue; }
}

...which will evaluate to (like classic Less):

.foo.bar { color: blue; }
8. Better :extend -- converts to :is() wrappers.

Example:

.one.two.three {
  color: blue;
}

.foo:extend(.one);
.bar:extend(.two.three);
.four:extend(.two);
.five {
  :extend(.one.two.three !all);
}

would evaluate to:

:is(:is(.one, .foo):is(:is(.two, .four).three, .bar), .five) {
  color: blue;
}

Variable expansion in a selector

This should function similarly to above, where we don't have to combine imported variables with the other list. Meaning, we can just do:

@list: h2, a, li;

.child @{list} {
  color: blue
}

This will result in (the much less computationally-expensive):

.child :is(h2, a, li) {
  color: blue
}

Future feature: Extend should consider :is() and :where()

Given this:

:is(.foo) {
  color: blue;
}
.bar:extend(.foo);

This should result in:

:is(.foo, .bar) {
  color: blue;
}

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No specific files, tests, or entry points are named. Start by reviewing superseded issue #33 and the linked #4319 changes, then narrow the roadmap into a separately scoped proposal with explicit acceptance criteria; the current issue's many unresolved parser, warning, import, and engine changes do not define a single completion point.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
tooling, web-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
15/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.