mrkeyoor.com_
Sun 20 Sept 18:57 UTC
npmWeb Frontendupdated 20 Sept 2026

less review

Less compiles `.less` source into CSS. Its language adds variables, nesting, parametric mixins, guards, arithmetic, maps, loops, imports, and functions while accepting ordinary CSS syntax. The npm package includes the `lessc` CLI, a Promise-based `less.render` API for Node, and a browser build. Version 4.9.0 adds bracket lookup inside interpolated `@{...}` expressions, stops reparsing `escape()` results, and preserves media types while flattening nested media queries. Our package check found working CommonJS and ESM entry paths, but no TypeScript declarations and a sizable browser result.

Verdict

Less 4.9.0 installed in 1.7 seconds and left 19 packages using 8 MB in our sandbox, while a browser import measured 158.5 KB minified and 51.5 KB gzipped. Keep it for stylesheets that depend on Less scoping and mixins; for new work, compare plain CSS, PostCSS, or Sass before adopting its global preprocessor model.

We installed it

Lab card: what happened when we installed lessScreenshot of less documentation
Install✓ · 1.7s19 packages on disk · 8 MB
ImportESM import works · require() works · ESM package with exports map
Browser51.5 KBgzipped (158.5 KB minified), bundled with esbuild
Typesno TypeScript types found
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does less install cleanly?

Yes. In a fresh container with an empty cache, npm install less finished in 2 seconds, leaving 19 packages and 8 MB on disk. npm audit reported no known vulnerabilities.

How much does less add to a browser bundle?

51.5 KB gzipped (158.5 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does less work with both ESM and CommonJS?

Yes. Both import 'less' and require('less') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does less include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

less or sass: which should you use?

sass: Use it for a new preprocessor codebase that needs namespaced modules through @use and @forward. Less 4.9.0 installed in 1.7 seconds and left 19 packages using 8 MB in our sandbox, while a browser import measured 158.5 KB minified and 51.5 KB gzipped.

When should you not use less?

A new project only needs custom properties and nesting. Current CSS plus a targeted PostCSS setup avoids Less scoping rules and a separate compiler language.

API stability4/5The 4.x compiler, `lessc`, `less.render`, variables, mixins, and import model remain familiar, and version 4.9.0 is a focused feature-and-fix release. The project is also preparing removals for Less 5: recent releases warn about bare variables in some at-rule positions, invalid legacy identifiers, and dynamic charset interpolation. Old themes should treat warnings as migration work.
Docs4/5The official site documents the language, built-in functions, command-line options, browser usage, Node API, plugins, and programmatic options with examples. The repository README is mostly a signpost, so important behavior such as math modes, URL handling, and JavaScript execution lives on separate pages. The changelog is detailed enough to identify 4.9.0 output fixes and upcoming syntax removals.
Maintenance4/5Less 4.9.0 was published on August 13, 2026, and GitHub recorded a push that day. The unarchived repository had 17,029 stars and 181 open issues and pull requests. Several 4.x releases arrived during 2026 with parser, browser export, security dependency, and CSS compatibility fixes. The issue backlog is material, but the recent release cadence is active.
Ecosystem4/5The npm downloads endpoint counted 11,737,289 installs for August 19 through August 25, 2026. Vite can compile Less after the package is installed, webpack has less-loader, and older theme systems still rely on variable overrides. New component tooling increasingly starts from plain CSS, Sass, or utility classes, so most ecosystem value comes from compatibility with existing Less assets.

Use it if

  • You maintain an existing Less theme and need its mixins, guards, imports, and variable override behavior to stay compatible.
  • A Node build tool or document generator must compile user-selected variables through `less.render`.
  • A framework integration already expects Less options such as `modifyVars`, `globalVars`, or `javascriptEnabled`.
  • Your team wants a CSS-like preprocessor without adding a non-Node compiler to the toolchain.
Skip it if

Setup reality

We installed Less 4.9.0 in a fresh Node 22 Bookworm container. npm succeeded in 1.7 seconds, left 19 packages using 8 MB, and reported no known vulnerabilities at any severity. The package declares 2 direct dependencies, no peer dependencies, 3,508 KB unpacked, Apache-2.0 licensing, and Node 18 or newer. It is ESM with an exports map; both require() and ESM import worked. No TypeScript declarations were present.

The normal production path compiles during a build or in Node. lessc comes with the package, while webpack needs less-loader and Vite only needs Less installed before it can process .less imports. Pass filename to less.render when the input is a string, because relative imports and URL rewriting need a base path. Static minification requires a plugin such as less-plugin-clean-css rather than a core lessc switch.

Less 4 defaults to parens-division, so @width / 2 outside parentheses may remain CSS instead of becoming a calculated value. Inline backtick JavaScript is disabled unless javascriptEnabled is set. Older themes often depend on both behaviors. Variable resolution is lazy and the last definition in a scope wins, which makes modifyVars useful for theming but can make import order surprising.

The package can run in a browser, though our esbuild import measured 158.5 KB minified and 51.5 KB gzipped. Compile ahead of time unless runtime theme generation truly needs the compiler. Version 4.9.0 changes interpolation lookup and fixes escape() plus nested media output, so compare generated CSS when upgrading a theme that uses those constructs.

Patterns

Compile an entry file and source map compile-with-cli

npx lessc src/styles.less dist/styles.css
npx lessc --source-map=dist/styles.css.map src/styles.less dist/styles.css

npm install --save-dev less-plugin-clean-css
npx lessc --clean-css src/styles.less dist/styles.min.css

Core Less does not include a minifier. The clean-css flag works only after its plugin is installed, and source maps require an explicit option.

Compile a Less string from Node render-in-node

import less from 'less';
import { readFile } from 'node:fs/promises';

const input = await readFile('src/styles.less', 'utf8');
const output = await less.render(input, {
  filename: 'src/styles.less',
  paths: ['src', 'node_modules'],
  math: 'parens-division',
});

console.log(output.css);
console.log(output.imports);

Set `filename` even for string input. It anchors relative imports, URL rewriting, source-map locations, and the returned dependency list.

Make division explicit under Less 4 control-division

@gutter: 24px;

.card {
  padding: (@gutter / 2);
  width: @gutter * 4;
}

The default `parens-division` mode divides inside parentheses. A bare slash may be preserved for native CSS instead of raising an error.

Account for lazy, last-wins variables scope-variables

@brand: blue;

.header {
  color: @brand;
  @brand: green;
}

@brand: red;
.footer { color: @brand; }

The header resolves to green because the later definition wins inside that scope. Imports can create the same silent collision across files.

Use parameters and a guard in a mixin define-guarded-mixin

.button-variant(@bg; @radius: 4px) when (iscolor(@bg)) {
  background: @bg;
  border-radius: @radius;
  color: if((lightness(@bg) > 50%), #111, #fff);
}

.btn-primary { .button-variant(#2563eb); }
.btn-danger { .button-variant(#dc2626; 8px); }

Semicolons separate mixin arguments safely. Commas can belong to a CSS value and may be parsed as one argument.

Choose how each imported file behaves control-import-output

@import (reference) '../theme/variables.less';
@import (optional) './local-overrides.less';
@import (css) 'https://fonts.example.com/x.css';
@import (inline) './legacy.css';

A reference import exposes variables and mixins without emitting all of its rules. Inline mode copies content without asking the Less parser to understand it.

Interpolate names and map lookups interpolate-lookup

@sizes: { sm: 4px; md: 8px; };
@choice: md;
@prefix: card;

.@{prefix} {
  gap: @{sizes}[@{@choice}];
}

Version 4.9.0 adds bracket lookups inside `@{...}` interpolation. Check the exact syntax when a dynamic key comes from another variable.

Share a rule through selector extension extend-selector

.button { padding: 8px 16px; border: 0; }
.button:hover { opacity: .9; }

.button-link {
  &:extend(.button all);
  background: none;
}

Extension adds selectors to existing output and keeps the original rule position. That source order can behave differently from copying declarations with a mixin.

Create utility rules from a detached map generate-from-map

@spaces: {
  sm: 4px;
  md: 8px;
  lg: 16px;
};

each(@spaces, {
  .p-@{key} { padding: @value; }
});

.stack { gap: @spaces[md]; }

Inside `each`, Less supplies `@key`, `@value`, and `@index`. Generated utility sets increase both compile work and final CSS.

Compile a theme with variable overrides override-theme-vars

import less from 'less';

async function buildTheme(primary) {
  const { css } = await less.render('@import "theme/index.less";', {
    filename: 'virtual.less',
    paths: ['src'],
    modifyVars: { '@primary-color': primary },
  });
  return css;
}

`modifyVars` places overrides after the source variables. Cache repeated results; recompiling an entire theme for each request wastes CPU.

Pass Less options through Vite configure-vite

// vite.config.js
export default {
  css: {
    preprocessorOptions: {
      less: {
        math: 'parens-division',
        javascriptEnabled: false,
      },
    },
  },
};

Install Less in the project and Vite detects `.less` imports. Enable inline JavaScript only for a reviewed legacy theme that needs it.

Add a compiler function with a plugin register-plugin-function

// double-plugin.cjs
module.exports = {
  install(less, pluginManager, functions) {
    functions.add('double', (value) =>
      new less.tree.Dimension(value.value * 2, value.unit));
  },
};

// styles.less
@plugin './double-plugin.cjs';
.box { width: double(20px); }

Plugin functions receive and return Less tree nodes. A reviewed plugin is safer than turning on arbitrary backtick JavaScript in stylesheets.

Alternatives

PackageRegistryPick it when
sassnpmUse it for a new preprocessor codebase that needs namespaced modules through `@use` and `@forward`.
postcssnpmUse it to keep CSS syntax and apply only selected transforms such as nesting or autoprefixing.
stylusnpmUse it when optional-brace syntax and a JavaScript plugin API outweigh its smaller current community.
lightningcssnpmUse it for fast CSS transforms, minification, and browser-target handling without Less language features.

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.