Skip to content

@fun

@fun lets you define reusable CSS blocks. The defined function is called with parameters and expanded at compile time.


@fun: function-name(param1, param2) {
/* body */
}

Rules:

  • - can be used in function names
  • @for, @if, elseif, else can be used inside the body
  • If argument count is too few or too many, the function is not expanded and a function-argument-mismatch warning is produced

Argument mismatch example:

@fun: make-chip(name, bg, fg) {
.chip-${name} {
background: ${bg};
color: ${fg};
}
}
/* ✓ Correct — 3 arguments */
@fun.make-chip(primary, #dbe9ff, #1c3f8f);
/* ✗ Too few arguments — not expanded, function-argument-mismatch warning produced */
@fun.make-chip(primary, #dbe9ff);
/* ✗ Too many arguments — not expanded, function-argument-mismatch warning produced */
@fun.make-chip(primary, #dbe9ff, #1c3f8f, 8px);

On mismatch, the relevant @fun.make-chip(...) call does not appear in CSS output; other correct calls are unaffected.


The function body generates selector blocks or properties. Called as an independent statement.

@fun: make-chip(name, bg, fg) {
.chip-${name} {
background: ${bg};
color: ${fg};
padding: 4px 12px;
border-radius: 12px;
}
}
@fun.make-chip(success, #d1fae5, #065f46);
@fun.make-chip(danger, #fee2e2, #991b1b);

Generated CSS:

.chip-success {
background: #d1fae5;
color: #065f46;
padding: 4px 12px;
border-radius: 12px;
}
.chip-danger {
background: #fee2e2;
color: #991b1b;
padding: 4px 12px;
border-radius: 12px;
}

The function body produces a value expression. Called as the value of a property.

@fun: size(i) {
${i * 4}px;
}
.btn {
padding: @fun.size(3);
margin: @fun.size(2);
}

Generated CSS:

.btn {
padding: 12px;
margin: 8px;
}

The function body is written as property: value form. Called as a statement; expanded as the full property block.

@fun: font-size-s(i) {
font-size: 12px * ${i};
}
.title-s {
color: #000;
@fun.font-size-s(2);
}

Generated CSS:

.title-s {
color: #000;
font-size: 24px;
}

Multiple property: value can be written:

@fun: sizes(i) {
font-size: 12px * ${i};
margin: ${i * 4px};
}
.title-s {
color: #000;
@fun.sizes(2);
}

Generated CSS:

.title-s {
color: #000;
font-size: 24px;
margin: 8px;
}

Variable values can be passed to function parameters via var(...).

--primary: #dbe9ff;
--secondary: #1c3f8f;
--call: 5;
@fun: make-chip(name, bg, fg, call) {
.chip-${name} {
background: ${bg};
color: ${fg};
margin: ${call * 2}px;
}
}
@fun.make-chip(primary, var(--primary), var(--secondary), var(--call));

Generated CSS:

:root {
--primary: #dbe9ff;
--secondary: #1c3f8f;
--call: 5;
}
.chip-primary {
background: var(--primary);
color: var(--secondary);
margin: 10px;
}

Part of the variable name inside var(...) can also be generated via a placeholder:

--colors-light: { bg: #f8f9fa; text: #212529; };
--colors-dark: { bg: #0f1117; text: #e9ecef; };
@fun: theme-bg(t) {
background-color: var(--colors-${t}.bg);
color: var(--colors-${t}.text);
}
.card-light { @fun.theme-bg(light); }
.card-dark { @fun.theme-bg(dark); }

Generated CSS:

.card-light {
background-color: #f8f9fa;
color: #212529;
}
.card-dark {
background-color: #0f1117;
color: #e9ecef;
}

A function can be called from inside a @for loop. The function is expanded independently for each iteration.

@fun: make-density(mode) {
@if(${mode} === 'compact') {
.density-${mode} { padding: 2px 6px; }
}
else {
.density-${mode} { padding: 6px 10px; }
}
}
@for(mode, [compact, relaxed]) {
@fun.make-density(${mode});
}

Generated CSS:

.density-compact { padding: 2px 6px; }
.density-relaxed { padding: 6px 10px; }

The function body can also contain nested selectors and at-rules:

@fun: card-layout(bp, pad) {
@media (min-width: ${bp}) {
.card {
padding: ${pad};
.title {
font-size: 18px;
}
}
}
}
@fun.card-layout(768px, 24px);

Generated CSS:

@media (min-width: 768px) {
.card {
padding: 24px;
}
.card .title {
font-size: 18px;
}
}

This pattern is suitable for reusing the same nested-at-rule pattern with different breakpoints or values.


Local variables can be defined inside @fun bodies with the $name syntax. Local variables are accessed with ${name}.

@fun: make-spacing(base) {
$sm: ${base};
$md: ${base * 2};
$lg: ${base * 4};
.space-sm { margin: ${sm}px; }
.space-md { margin: ${md}px; }
.space-lg { margin: ${lg}px; }
}
@fun.make-spacing(4);

Generated CSS:

.space-sm { margin: 4px; }
.space-md { margin: 8px; }
.space-lg { margin: 16px; }

SituationWarning
Undefined function calledunknown-function-call
Argument count mismatchfunction-argument-mismatch
Two functions with the same name definedduplicate-function
Invalid function syntaxinvalid-function-syntax
Invalid function nameinvalid-function-name
Recursionfunction-recursion