EthicsComputerCourses
Join training +91 90532 10052

Built-in Modules: math, string & meta

Sass · Lesson 17 of 26 Advanced 6 min read Updated

Sass ships with a standard library of ready-made functions, grouped into built-in modules. You have already met sass:map, sass:list and math.div(). This lesson tours the rest of sass:math, the text tools in sass:string, and sass:meta, which lets your code inspect values and other code. Colours have their own lesson next: Colour Functions & Colour Spaces.

You load a built-in module like any other module, with @use "sass:name", and call its members through the namespace. Built-in modules are always available, so every example in this lesson runs in the editor.

The seven built-in modules

ModuleTypical useExamples
sass:mathNumbers and unitsmath.div(), math.round(), math.clamp(), math.$pi
sass:stringTextstring.slice(), string.index(), string.to-upper-case()
sass:colorColourscolor.scale(), color.mix(), color.to-space()
sass:listListslist.nth(), list.append(), list.join()
sass:mapMapsmap.get(), map.merge(), map.deep-merge()
sass:selectorSelectorsselector.nest(), selector.append()
sass:metaTypes and codemeta.type-of(), meta.call(), meta.load-css()

sass:math

Besides math.div(), the maths module rounds numbers, picks the largest or smallest, and checks units:

SCSSTry it yourself
@use "sass:math";

@debug math.round(4.6px);
@debug math.ceil(4.2);
@debug math.floor(4.8);
@debug math.max(3px, 9px, 5px);
@debug math.clamp(10px, 4px, 20px);
@debug math.pow(2, 10);
@debug math.sqrt(16);
@debug math.unit(16px);
@debug math.is-unitless(1.5);
@debug math.compatible(1in, 1px);

$circle: 120px;

.ring {
  width: $circle;
  stroke-dasharray: math.round(math.$pi * $circle);
}
Output
line 3 DEBUG: 5px
line 4 DEBUG: 5
line 5 DEBUG: 4
line 6 DEBUG: 9px
line 7 DEBUG: 10px
line 8 DEBUG: 1024
line 9 DEBUG: 4
line 10 DEBUG: px
line 11 DEBUG: true
line 12 DEBUG: true
.ring {
  width: 120px;
  stroke-dasharray: 377px;
}

Notes on the less obvious ones:

  • math.clamp($min, $value, $max) keeps a value inside a range while compiling: 4px was raised to the minimum 10px. It is different from CSS clamp(), which the browser works out live.
  • math.unit() returns a number's unit as a string, and math.is-unitless() checks for none. Both are useful for checking arguments in your own functions.
  • math.compatible() tells you whether two numbers can be added: in and px can, px and rem cannot.
  • math.$pi is a module variable, written with the namespace before the $.

There is also math.random(), which gives a different number on every compile. Avoid it in real stylesheets, because each build changes your CSS for no reason.

sass:string

String functions let you take apart and build names. Positions start at 1, like lists:

SCSSTry it yourself
@use "sass:string";

$class: "ec-btn-primary";

@debug string.length($class);
@debug string.index($class, "btn");
@debug string.slice($class, 4, 6);
@debug string.insert("btn", "ec-", 1);
@debug string.to-upper-case("pune");
@debug string.split($class, "-");
@debug string.unquote(".card");
@debug string.quote(bold);

.badge::after {
  content: string.to-upper-case("new");
}
Output
line 5 DEBUG: 14
line 6 DEBUG: 4
line 7 DEBUG: btn
line 8 DEBUG: ec-btn
line 9 DEBUG: PUNE
line 10 DEBUG: ["ec", "btn", "primary"]
line 11 DEBUG: .card
line 12 DEBUG: bold
.badge::after {
  content: "NEW";
}
  • string.index() finds where a piece starts (or returns null).
  • string.slice($string, $start, $end) cuts out a piece; both ends are included.
  • string.split() (Dart Sass 1.57 and later) breaks a string into a bracketed list.
  • string.quote() and string.unquote() add or remove quotes. CSS needs quotes in some places (content, font names with spaces) and not in others (selectors, keywords).

sass:meta

sass:meta lets your code ask questions about values and other code. The most useful members for everyday work:

SCSSTry it yourself
@use "sass:math";
@use "sass:meta";

@function rem($px) {
  @if meta.type-of($px) != number or math.unit($px) != "px" {
    @return $px;
  }
  @return math.div($px, 16px) * 1rem;
}

@debug meta.type-of(12px);
@debug meta.type-of("Lucknow");
@debug meta.type-of(#cc6699);
@debug meta.type-of((a: 1));
@debug meta.inspect((brand: #cc6699, space: (sm: 4px)));
@debug meta.function-exists("rem");

.title {
  font-size: rem(24px);
  margin: rem(auto);
  padding: meta.call(meta.get-function("rem"), 8px);
}
Output
line 11 DEBUG: number
line 12 DEBUG: string
line 13 DEBUG: color
line 14 DEBUG: map
line 15 DEBUG: (brand: #cc6699, space: (sm: 4px))
line 16 DEBUG: true
.title {
  font-size: 1.5rem;
  margin: auto;
  padding: 0.5rem;
}
  • meta.type-of() returns number, string, color, list, map, bool, null and a few others. The rem() function uses it to pass values like auto through untouched instead of failing.
  • meta.inspect() turns any value into a readable string, which is handy inside @debug and error messages.
  • meta.function-exists(), meta.variable-exists() and meta.mixin-exists() check whether a name is defined at this point.
  • meta.get-function() turns a function into a value you can store or pass around, and meta.call() calls it. Libraries use this to let users pass in their own functions.

One member of sass:meta needs real files, so it is shown as plain code: meta.load-css() is a mixin that loads another module's CSS inside a rule, which is handy for scoping a whole theme under a class:

SCSS
@use "sass:meta";

.theme-dark {
  @include meta.load-css("themes/dark");
}

Every rule from themes/_dark.scss is written with .theme-dark in front of it. Unlike @use, it can appear anywhere, and it accepts a $with map to configure the module.

Common mistakes

  • Forgetting the @use line. Without @use "sass:string";, string.slice() is an error (the message is There is no module with namespace "string".).
  • Counting from 0 in strings. The first character is position 1.
  • Confusing math.clamp() with CSS clamp(). The first is fixed at compile time; the second responds to the screen.
  • Comparing units with == on strings. math.unit() returns a quoted string, so compare with "px", not px.

Key points

  • Built-in modules are loaded with @use "sass:math" and so on, and their members use the namespace: math.round(), math.$pi.
  • sass:math rounds, clamps and checks units; math.compatible() tells you if two units can be combined.
  • sass:string measures, searches, slices, splits and changes the case of strings, counting from 1.
  • sass:meta checks types (meta.type-of()), prints values (meta.inspect()), and calls functions dynamically (meta.call()).
  • meta.load-css() includes another module's CSS inside a rule, which is useful for scoped themes.

Exercise

Write a function px-to-rem($value) for a Kochi news site that converts only pixel numbers to rem (16px base) and returns everything else unchanged. Use meta.type-of() and math.unit(). Test it with @debug on 24px, 1.5em, 0 and auto. Then use it on .headline for a 28px font size and for both parts of margin: 0 auto (call it once for each value).

Show solution
SCSS
@use "sass:math";
@use "sass:meta";

@function px-to-rem($value) {
  @if meta.type-of($value) == number and math.unit($value) == "px" {
    @return math.div($value, 16px) * 1rem;
  }
  @return $value;
}

@debug px-to-rem(24px);
@debug px-to-rem(1.5em);
@debug px-to-rem(0);
@debug px-to-rem(auto);

.headline {
  font-size: px-to-rem(28px);
  margin: px-to-rem(0) px-to-rem(auto);
}

Only 24px and 28px are converted (to 1.5rem and 1.75rem). 1.5em, the unitless 0 and the keyword auto fail the check and come back unchanged.

Quick Quiz

1. How do you read the value of pi from the math module?

2. What does string.slice("ec-btn-primary", 4, 6) return?

3. What does meta.type-of(#cc6699) return?

4. Which is true about math.clamp(10px, 4px, 20px)?

Learn Sass with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call