Comments (23)
@jbrooksuk: Two main reasons I will personally always prefer Less over Sass:
1. Evaluation order
Less is evaluated like a declarative definition of your styles (just like CSS is), while Sass is evaluated like a procedural script.
Consider this example:
.btn {
color: blue;
border-color: currentColor;
color: red;
}
When the above CSS is applied, the border color will be red, not blue, because the final evaluated value of the selector's color
property is red.
Consider this Sass:
.btn {
$color: blue;
border-color: $color;
$color: red;
}
In that example, the border color will be blue, because Sass evaluates your styles from top to bottom like a procedural script, using whatever the state of each variable is along the way.
Here's the same thing in Less:
.btn {
@color: blue;
border-color: @color;
@color: red;
}
Just like real CSS, in Less, the border-color here will be red, not blue, because Less evaluates your styles and uses the final evaluated variable value.
This problem rears its head in many ways in Sass, including the stupid !default
hack that was eventually introduced because of how sensitive Sass is to inclusion order and evaluation order.
The order in which styles are defined already has important semantic meaning in CSS. Selectors that are defined later will defeat selectors that are defined earlier that have the same specificity.
So with Sass, you have an on-going battle between two different reasons to define things in a certain order, making your stylesheets more complicated and more difficult to maintain.
You also have to change your entire mental model of how styles are defined because Sass does not work like CSS, whereas Less works exactly like CSS.
Another example, in Sass, mixins need to be defined before they are used:
.foo {
@include bg-primary();
}
@mixin bg-primary() {
background: green;
}
// ERROR: Undefined mixin 'bg-primary'.
In Less, this works perfectly fine:
.foo {
.bg-primary();
}
.bg-primary() {
background: green;
}
// Sure bro, I got your back! 😎
This might seem like an arbitrary choice at first, but when you compare it with how CSS works, it becomes obvious that Less is more consistent the expected behavior of real CSS.
For example, keyframes can be defined after they are used with no problem at all:
.this-spins {
animation: spin 500ms infinite linear;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
Again, this is because CSS is a declarative definition of your styles, and Sass is a procedural script evaluated with state and control flow.
This sort of inconsistency means that when someone is learning Sass, they are building an incorrect mental model of how CSS works, and for that reason I think it's better to use Less, which behaves the same way CSS does.
More reading:
2. Classes as mixins
A crucial feature in Less that I rely on every day is the ability to use a class as a mixin.
Say I have these utility classes defined:
.py-2 {
padding-top: .5rem;
padding-bottom: .5rem;
}
.px-4 {
padding-top: .5rem;
padding-bottom: .5rem;
}
.bg-primary {
background-color: @color-primary;
}
.text-medium {
font-weight: 600;
}
.text-light {
color: #fff;
}
(...along with many others.)
I might combine those utilities in my markup to style a button:
<button class="bg-primary text-medium text-light py-2 px-4">Save</button>
Over time, maybe I find myself using this same combination of utilities fairly regularly as a standard button style.
"Graduating" this pattern of classes to a component in Less is trivial, because classes in Less are composable:
Here's the resulting definition for the .btn
class:
.btn {
.bg-primary;
.text-medium;
.text-light;
.py-2;
.px-4;
}
...which compiles to:
.btn {
background-color: @color-primary;
font-weight: 600;
color: #fff;
padding-top: .5rem;
padding-bottom: .5rem;
padding-top: .5rem;
padding-bottom: .5rem;
}
In Sass, you have a few options:
1. Extend your utilities
Here's a quick example with fewer declarations:
.btn {
@extend .bg-primary;
// ...
}
.bg-primary {
background: blue;
}
This will generate the following CSS
.bg-primary, .btn {
background: blue;
}
This might seem fine at first glance but look at this example:
.btn {
@extend .bg-primary;
// ...
}
.btn-warning {
background: yellow;
}
.bg-primary {
background: blue;
}
...which generates:
.btn-warning {
background: yellow;
}
.bg-primary, .btn {
background: blue;
}
Notice how the declaration of .btn
is now below the declaration of .btn-warning
?
That means that in this HTML, the button will be blue, not yellow:
<button class="btn btn-warning">Save</button>
So you can't extend a utility unless you define that utility earlier as well, which means the utility won't defeat component styles unless you add !important
. Or you could use silent classes/placeholders, but you end up with the same out of control verbosity that you'll see in the next example.
This is grim as fuck, don't use @extend
in Sass or in Less.
More reading:
- When to use @extend, when to use a mixin
- Mixins Better for Performance
- Why You Should Avoid Sass @extend
2. Build your utilities out of mixins
Another option is to create an explicit mixin for every single utility class so you can reuse that mixin in the new component:
@mixin py-2() {
padding-top: .5rem;
padding-bottom: .5rem;
}
@mixin px-4() {
padding-top: .5rem;
padding-bottom: .5rem;
}
@mixin bg-primary() {
background-color: $color-primary;
}
@mixin text-medium() {
font-weight: 600;
}
@mixin text-light() {
color: #fff;
}
.btn {
@include bg-primary();
@include text-medium();
@include text-light();
@include py-2();
@include px-4();
}
.py-2 {
@include py-2();
}
.px-4 {
@include px-4();
}
.bg-primary {
@include bg-primary();
}
.text-medium {
@include text-medium();
}
.text-light {
@include text-light();
}
This is so much more work than it is in Less that there's no way I could ever justify it.
This approach to composing components out of repeated patterns of utilities is the basis of my entire philosophy to CSS, so I can't use a pre-processor that makes it this hard to do.
More subjective reasons
A few other things I much prefer in Less:
1. Mixin syntax
Compare Sass:
@mixin text-truncate() {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card-heading {
@include text-truncate();
}
to Less:
.text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card-heading {
.text-truncate;
}
Less crap to type is better than more crap to type IMO.
2. Functional vibe
Less feels more "sophisticated" to me than Sass. I like that I can use pattern matching, guard clauses, and recursion instead of if statements and loops. It's more fun and feels like a functional programming language. Helps keep me sharp :)
Closing
I prefer Less because it is evaluated declaratively instead of procedurally, supports class composition, and feels more like a functional programming language.
Sass has a handful of features that Less doesn't have, but I've never found myself needing them. Less does the features that I do need better than Sass does them for my workflow, so I will take Less to the grave with me :)
from horizon.
Sass is awful, Less is much better 🤷♂️
from horizon.
-1
Fact: Less roxx more. Look at @adamwathan's #56 (comment)
Just to clarify why I originally wrote "no explanation needed": @lucasmichot said BS 4 will use SASS. What kind of explanation is that? So I just adjusted my comment to @lucasmichot's scope. There are plenty of great frameworks/tools that use LESS.
from horizon.
@adamwathan on the topic of:
Less crap to type is better than more crap to type IMO.
Those code blocks aren't equivalent though.
In SASS:
@mixin text-truncate() {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card-heading {
@include text-truncate();
}
Will output:
.card-heading {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
While your LESS example:
.text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card-heading {
.text-truncate;
}
Will output:
.text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card-heading {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
This means unused mixins/utilitiy classes are still output in the final CSS.
This is not a bug, but something to be aware of in the way the syntax has ambiguity.
from horizon.
This means unused mixins/utilitiy classes are still output in the final CSS.
You can easily avoid this by throwing parentheses at the end of the utility name:
.text-truncate() {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
Personally I want them generated and output though because I use them all over my markup.
EDIT: Another way you can avoid them being rendered is using @import (reference)
and putting the utilities in a different file. Can be handy if you want to use classes from a third party library as mixins without outputting them. Used to do that with Bootstrap a lot.
from horizon.
I really hope we used BS4 in Horizon :)
from horizon.
@lucasmichot css doesn't like css, css only likes to piss off people.
from horizon.
Hey @adamwathan just publish it in your blog already. Too much detail just to be in github comment section.
from horizon.
@jbrooksuk you would be more than welcome
I have never been enjoying CSS - and CSS doesn't like me either 😅
from horizon.
@adamwathan I know your love for less is well know, but this information makes it more fact than a simple "I use less because it is better than sass" general statement.
@kbirmhrjn I am actually glad it was here, as it is fits into the overall ticket request (which had a lame simple "because it is better") vibe. And the fact that BS4 uses it is a baseless reason.
from horizon.
I would say the variable evaluation is a philosophical difference and I see both sides of things.
The idea of LESS working more like CSS variables today is a 👍 if you know how CSS variables work:
body {
--card-bg-color: #fff;
}
.card {
background: variable(--card-bg-color);
}
body {
--main-bg-color: brown;
}
Would result in the card's background being brown.
However saying that it's good for LESS to treat it this way is a bit of misnomer since ☝️ doesn't tell the whole story.
In the example above, the card background is brown because it is inside of body and body has the custom variable --main-bg-color
set to brown at last evaluation.
But, there's a lot more to understand since variables are scoped to the local parent element so if there's a parent element to .card
that changes the value of --main-bg-color
then the card's background is no longer brown
but something else entirely.
This is something that without a component CSS implementation is near impossible to replicate statically.
In that IMO the last value evaluation used by LESS actually makes for a different mental model and something that could cause unexpected bugs.
Let's take the card from above:
@cardBg: #fff;
.card {
background: @cardBg;
}
Now in a later imported file the @cardBg
value is changed:
@cardBg: green;
.author-bio-thing {
background: @cardBg;
}
This has now created side-effects throughout the application that could be hard to trace especially in an OSS environment.
That's not to say that SASS is perfect in either of these senses since later imports inherit the changed variable value.
But, I think it matches a simpler mental model:
$red = "#f00";
echo("body: " . $red);
$red = "#e34242";
The code would print body: #f00
not body: #e34242
from horizon.
I think the difference is that Sass is a scripting language with state, Less is an evaluated declarative definition of your styles. Sass is easier to understand if you want to think of your styles like a programming script, Less is easier to understand if you are used to CSS.
Another example of order declaration in Less matching CSS is keyframes; in CSS keyframes can be defined after they are used, all that matters is that they exist at the end of the day when the stylesheet is being evaluated. Less works the same way, processing your CSS using one final state, not a ton of intermediate state.
from horizon.
Yeah, good idea. Laravel and BS v4 use Sass, let's stick with the same stack.
from horizon.
I'm quite happy to implement this if no-one else wants to?
from horizon.
Ok! On it 😊
from horizon.
😂😂😂😂 ❗ 🤣
from horizon.
LESS is.... less than favorable.
from horizon.
On what basis @adamwathan? For consistency, Laravel is Scss :)
from horizon.
from horizon.
@adamwathan thanks for the in-depth analysis :)
from horizon.
Yes @adamwathan 👍
from horizon.
Sass is easier to understand if you want to think of your styles like a programming script, Less is easier to understand if you are used to CSS.
That puts the difference in mindset. I personally see it as SASS is aware that it's a static compilation target, LESS is still inheriting from being a browser runtime language. While there are things that LESS is more like CSS running in browser, there are still alot of ways where it stops short and I find those edges can be harder to grasp than a scripting/compilation style model that SASS provides.
from horizon.
Nice details about the differences. Thanks all!
from horizon.
Related Issues (20)
- Distribute jobs to 4 processors HOT 1
- Job feedback HOT 1
- Ability to cancel currently running job batches HOT 3
- Pending job number seems incorrect HOT 7
- Monitoring tags consumes more Redis RAM than expected HOT 6
- New command `horizon:clear-metrics` does not work HOT 3
- Since 2.4 - addSecon() is deprecated. Use addSeconds() instead. HOT 3
- long time job can't not execute entirely HOT 1
- pass arguments to horizon:work and horizon:supervisor HOT 1
- Complete restart of workers when using `horizon:terminate` cmd HOT 1
- Failure on retrieving job metric details in the horizon dashboard HOT 3
- Adding Horizon Flush Command to delete all keys on `horizon` connection. HOT 1
- Your Composer dependencies require a PHP version ">= 8.1.0". You are running 7.4.33. PHP Fatal error: Composer detected issues in your platform: Your Composer dependencies require a PHP version ">= 8.1.0". You are running 7.4.33. in /myProject/vendor/composer/platform_check.php on line 24 Composer detected issues in your platform: HOT 2
- Multiserver setup with Forge and Horizon HOT 5
- Jobs are slow to be picked up, at least 2 seconds of extra processing time HOT 2
- Expected Throughput - Ability to see delayed job count as time series HOT 1
- Queues keep crashing HOT 1
- Endpoint `/horizon/api/jobs/completed?starting_at=-1&limit=50` keeps returning empty results HOT 3
- Jobs causing memory overflow do not consider $maxExceptions (and are retried endlessly if $tries=0) HOT 10
- Disable all jobs lists HOT 9
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from horizon.