Building Reusable UI Components with Parametric Less Mixins and Guards
Learn how to eliminate duplicated button styles by wrapping shared CSS in a parametric Less mixin and using guards to inject theme‑ or state‑dependent values.
05 Jul 2025, 15:38 UTC

When a UI library expands, the same button or card appearance is repeated with only a few tweaks—different background colour, a slightly larger radius, or a hover shade. This copy‑paste approach inflates the stylesheet, makes global changes error‑prone, and obscures the intent of each variant.
The takeaway: encapsulate the shared structure in a parametric Less mixin and use guards to inject theme‑ or state‑dependent values. The result is a single source of truth that adapts to any number of variants while keeping the call site readable.
Defining a parametric mixin
A parametric mixin works like a function: it accepts arguments and outputs a block of CSS. The mixin below contains the structural rules that every button needs—padding, font size, border‑radius, and a hover effect—while delegating colours and radius to the caller.
.button(@bg, @fg, @radius: 4px) {
display: inline-block;
padding: 10px 20px;
font-size: 16px;
border: none;
border-radius: @radius;
background-color: @bg;
color: @fg;
cursor: pointer;
transition: background-color 0.2s ease;
}
The mixin can be invoked from any selector, passing the values that differentiate the button.
Adding guards for conditional values
Sometimes the same component must choose a palette based on a theme flag rather than an explicit colour variable. Less guards let you attach a conditional expression to a mixin body; the block runs only when the guard evaluates to true.
.button-themed(@theme) {
when (@theme = 'dark') {
@bg: #2b2b2b;
@fg: #f0f0f0;
}
when (@theme = 'light') {
@bg: #ffffff;
@fg: #1a1a1a;
}
// reuse the structural mixin
.button(@bg, @fg, 4px);
}
The guard keeps the theme decision inside the mixin, so the call site stays simple: .btn-primary { .button-themed('dark'); }.
Worked example: generating primary and outline buttons
Create three files in a project folder:
variables.less– colour palettemixins.less– the.buttonmixin and a guard‑based variantstyles.less– imports and call sites
/* variables.less */
@primary: #0069d9;
@secondary: #6c757d;
@white: #ffffff;
@gray: #e9ecef;
/* mixins.less */
.button(@bg, @fg, @radius: 4px) {
display: inline-block;
padding: 12px 24px;
font-size: 15px;
border-radius: @radius;
background-color: @bg;
color: @fg;
font-weight: 600;
cursor: pointer;
transition: background-color 0.15s ease-in-out;
&:hover {
background-color: rgba(0,0,0,0.07);
}
}
/* guard‑based theme helper (optional) */
.button-dark() {
when (@theme = dark) {
.button(#222, #fff, 4px);
}
}
/* styles.less */
@import "variables.less";
@import "mixins.less";
.btn-primary { .button(@primary, @white, 4px); }
.btn-secondary { .button(@secondary, @white, 2px); }
.btn-outline {
.button(@transparent, @primary, 2px);
border: 2px solid @primary;
background-color: transparent;
}
To compile, open a terminal in the folder and run:
lessc styles.less styles.css
No special permissions are required; the command reads the local files and writes styles.css. Verify the output by opening styles.css and confirming that:
- All three button classes share identical padding, font‑size, transition, and hover rules.
- Only the
background-color,color, andborder-radiusdiffer as passed at the call site. - No duplicate blocks appear for the shared properties.
If the file is large, you can speed up subsequent builds by adding the Less compiler to a watch script or using less-loader in Webpack, which caches parsed modules.
Trade‑off and limitation
Over‑parameterizing a mixin can make the call site verbose and hide the generated CSS, especially when many arguments are needed for minor tweaks. Teams should therefore:
- Limit the mixin to the truly invariant structural core.
- Expose only the dimensions that actually vary across components.
- Document the expected argument order or use named‑style calls via intermediate variables for clarity.
A practical check is to diff the CSS produced from the mixin version against a hand‑written version; if the diff shows only the expected property changes, the abstraction is sound.
Actionable closing
Start by extracting the repeated padding, font‑size, and transition rules from your existing button classes into a parametric mixin. Add a guard only when you need theme‑based colour selection. Compile with lessc and inspect the generated CSS to verify reuse. This approach cuts duplication, simplifies global updates, and keeps the stylesheet easy to read.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.