Why LESS Matters on Squarespace
LESS is the only CSS preprocessor available in Squarespace Developer Mode. There is no option for Sass, PostCSS, or any other tool. Squarespace compiles your LESS files server-side when you push to GitHub, and the output is served as the site's stylesheet.
This is not the latest version of LESS. Squarespace uses an older compiler (approximately LESS 1.3.x), which means some newer LESS features are unavailable. However, the features that matter most — variables, mixins, nesting, and operations — all work correctly.
Understanding how this compiler works, what it supports, and where it breaks is essential knowledge for any Dev Mode project. Most of the CSS debugging headaches in Dev Mode trace back to LESS compilation misunderstandings.
The Compilation Model
LESS compilation on Squarespace is controlled entirely by the stylesheets array in template.conf:
{
"stylesheets": [
"base.less",
"home.less",
"about.less",
"services.less"
]
}Each file in this array is compiled independently by the server. The outputs are concatenated into a single site.css file that gets injected into the page via {squarespace-headers}.
The critical word here is independently. Each LESS file is its own compilation unit. Variables defined in base.less are not available in home.less. Mixins defined in one file cannot be used in another. Each file sees only its own scope.
Key concept
Think of each entry in the stylesheets array as a completely separate LESS compilation. They share nothing. The only thing they share is the final CSS output file.
Why @import is Broken
In standard LESS, you can use @import to pull one file into another, sharing variables and mixins across files. On Squarespace, this does not work. The @import directive is silently ignored by the compiler.
/* base.less */
@brand-color: #1a1a1a;
/* home.less */
@import "base.less"; /* SILENTLY IGNORED */
.hero {
color: @brand-color; /* UNDEFINED — compilation fails */
}This fails because home.less never actually imports base.less. The @brand-color variable does not exist in home.less's scope, so the entire LESS compilation fails — silently.
Warning
When LESS compilation fails, you get zero custom CSS. The site renders with only Squarespace's default platform styles. There is no error message in the browser. The only way to see the error is to check site.css?minify=false and scroll to the bottom.
The workaround: list every LESS file individually in template.conf and use CSS custom properties (not LESS variables) when you need to share values across files.
Single-File vs Multi-File Approach
Single-file approach
Put everything in base.less. All your variables, mixins, global styles, and page-specific styles live in one file. The stylesheets array has one entry.
/* template.conf */
{ "stylesheets": ["base.less"] }
/* base.less — everything lives here */
@brand: #1a1a1a;
@spacing: 24px;
body { font-family: 'Inter', sans-serif; }
.ldp-page-home {
.hero { padding: @spacing * 3; color: @brand; }
}
.ldp-page-about {
.team-grid { gap: @spacing; }
}Advantages: All LESS variables are available everywhere. Mixins work across all styles. No scoping surprises. Simple to reason about.
Disadvantages: The file grows large on complex sites. Every change to any style re-compiles the entire file. Harder to navigate for teams.
Multi-file approach
Separate concerns into per-page files. Each file compiles independently, so you must use CSS custom properties to share design tokens.
/* template.conf */
{ "stylesheets": ["base.less", "home.less", "about.less"] }
/* base.less — define CSS custom properties */
:root {
--brand: #1a1a1a;
--spacing: 24px;
}
body { font-family: 'Inter', sans-serif; }
/* home.less — uses CSS vars, not LESS vars */
.ldp-page-home {
.hero {
padding: calc(var(--spacing) * 3);
color: var(--brand);
}
}
/* about.less */
.ldp-page-about {
.team-grid { gap: var(--spacing); }
}Advantages: Clean file separation. Easier to navigate. Each file is self-contained. Better for team workflows.
Disadvantages: LESS variables and mixins are not shared. You must duplicate any LESS-specific features in each file. CSS custom properties are the only sharing mechanism.
Recommendation
For most sites, start with the single-file approach. Move to multi-file only when your base.less exceeds a few hundred lines and multiple developers are working on the project.
Design Tokens: CSS Custom Properties vs LESS Variables
This is the most important decision in your LESS architecture. Both CSS custom properties and LESS variables have roles, but they behave very differently on Squarespace.
LESS variables
@brand: #1a1a1a;
@spacing: 24px;
.hero {
color: @brand;
padding: @spacing * 2; /* LESS does the math at compile time */
}LESS variables are resolved at compile time. They support arithmetic, color functions, and string interpolation. But they only exist within the single file where they are defined.
CSS custom properties
:root {
--brand: #1a1a1a;
--spacing: 24px;
}
.hero {
color: var(--brand);
padding: calc(var(--spacing) * 2); /* Browser does the math */
}CSS custom properties pass through the LESS compiler untouched and are resolved by the browser at runtime. They work across all LESS files because they live in the final CSS output.
When to use which
Use LESS variables for compile-time math, color manipulation, and values that never need to cross file boundaries. Use CSS custom properties for design tokens that must be shared across files, values that change at runtime (dark mode, media queries), and anything you want accessible in JavaScript.
File Organization
The standard file organization for a Squarespace Dev Mode project:
styles/
base.less # Design tokens, reset, typography, nav, footer
# Global components, all @keyframes
home.less # .ldp-page-home { ... }
about.less # .ldp-page-about { ... }
services.less # .ldp-page-services { ... }
contact.less # .ldp-page-contact { ... }Every per-page LESS file should be scoped under the page-specific class to avoid style collisions:
/* services.less */
.ldp-page-services {
.service-card {
padding: 32px;
background: var(--color-off-white);
}
.service-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 24px;
}
}Common Gotchas
One undefined variable kills ALL styles
This is the single most common Dev Mode disaster. If any LESS file in your stylesheets array references a LESS variable that does not exist, the compilation of that file fails. The output is an empty CSS block. Your site renders with zero custom styles.
To debug this, visit your site with ?minify=false appended to the CSS URL:
https://yoursite.com/site.css?minify=false
Scroll to the bottom. If compilation failed, you will see an error message like:
/* LESS compilation error: variable @brand-color is undefined in file services.less line 14 */
Critical
Always check site.css?minify=false after every push. This is the only way to catch LESS errors. There is no build step, no terminal output, and no browser console warning. The failure is completely silent.
Forgetting to scope per-page styles
If you write .hero { ... } in home.less without wrapping it in .ldp-page-home, that style applies to every page on the site. Always scope per-page LESS files under the page class.
Duplicate variable definitions
If you define the same LESS variable twice in the same file, the last definition wins. This is standard LESS behavior, but it can cause confusing bugs when you have hundreds of lines in a single file.
clamp(), min(), max() Work Fine
A common concern is whether modern CSS functions survive the LESS compiler. They do. The Squarespace LESS compiler passes through clamp(), min(), and max() without modification.
h1 {
font-size: clamp(32px, 5vw, 64px);
padding: max(24px, 4vw);
width: min(100%, 1200px);
}No escaping needed. No workarounds. Write them as you would in plain CSS and they compile correctly.
@keyframes Work Fine in LESS
There is a persistent myth that Squarespace's LESS compiler strips @keyframes from the output. This is false. The compiler handles @keyframes correctly and has for as long as Dev Mode has existed.
@keyframes fadeIn {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.hero {
animation: fadeIn 0.6s ease-out forwards;
}
.cta-button {
animation: pulse 2s ease-in-out infinite;
}Define all @keyframes in your LESS files. Do not inject them via JavaScript. Do not duplicate them in inline <style> blocks. The LESS file is the single source of truth for animations.
History
This myth likely originated from a misdiagnosis. If your @keyframes are defined in a file that has a LESS compilation error, the entire file output is empty — including the keyframes. It is not the keyframes that are stripped; the whole file failed to compile.