Skip to content

Commit

Permalink
Dart Sass Module support for 3.0 + added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pierreburel committed Nov 6, 2021
1 parent ff78cb9 commit 7177cf0
Show file tree
Hide file tree
Showing 12 changed files with 7,407 additions and 98 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -0,0 +1,3 @@
node_modules/
npm-debug.log
yarn-error.log
9 changes: 9 additions & 0 deletions .npmignore
@@ -0,0 +1,9 @@
.npmignore
.gitignore

node_modules/
npm-debug.log
yarn.lock

*.test.js
.travis.yml
4 changes: 4 additions & 0 deletions .travis.yml
@@ -0,0 +1,4 @@
language: node_js
cache: yarn
node_js:
- stable
93 changes: 61 additions & 32 deletions README.md
Expand Up @@ -2,67 +2,96 @@

Sass function and mixin to convert px in em.

Demo: [Sassmeister](http://sassmeister.com/gist/4481fa0aeeaf49ea9325) / [Codepen](http://codepen.io/pierreburel/pen/dPwXrr)
**Breaking change in 2.0**: now using [Sass Modules](https://sass-lang.com/blog/the-module-system-is-launched), using `@use` and `em` is renamed to `em.convert`. You could still use `@import` with no changes (see usage below), but **if you need LibSass and Ruby Sass support (both deprecated), you should stay on 1.0** (which works fine) or use the [PostCSS](https://github.com/pierreburel/postcss-em) version.

Compatibility: Sass 3.2+ (3.3+ for the mixin) and LibSass
Compatibility: [Dart Sass](https://sass-lang.com/dart-sass) only.

PostCSS version: https://github.com/pierreburel/postcss-em

See also: https://github.com/pierreburel/sass-rem

---

## Install

Download [`_em.scss`](https://raw.githubusercontent.com/pierreburel/sass-em/master/_em.scss) or install with [Bower](http://bower.io/) or [npm](https://www.npmjs.com/):
## Installation

```
bower install sass-em
```
Install with [Yarn](https://yarnpkg.com/) or [npm](https://www.npmjs.com/):

### npm

```
npm install sass-em
```
* `yarn add sass-em`
* `npm install sass-em`

---

## Usage

Import `_em.scss` and use the `em` mixin or function.
The `em.convert` function takes at least 2 parameters: the value(s) (px, mixed) and the context (px).
There can be multiple values (eg. multiple box shadow), but **the last parameter must be the context**.

The function takes at least 2 parameters: the value(s) (px, mixed) and the context (px).
There can be multiple values (eg. multiple box shadow), but **the last parameter must be the context**.
The `em.convert` mixin takes only 2 parameters: the properties (map of `property: value`) and the context (px). It can be used to convert the values of multiple properties with the same context.

The mixin takes only 2 parameters: the properties (map of `property: value`) and the context (px). It can be used to convert the values of multiple properties with the same context.

## Example
Import in your project depending of your setup:

```scss
@import "em";
@use "em";
// or @use "~sass-em" as em;
// or @use "../node_modules/sass-em" as em;

$base-font-size: 16px;
$h1-font-size: 24px;

h1 {
font-size: em($h1-font-size, $base-font-size); // Simple
border-bottom: em(1px solid black, $h1-font-size); // Shorthand
box-shadow: em(0 0 2px #ccc, inset 0 0 5px #eee, $h1-font-size); // Multiple values
// Mixin (Sass 3.3+)
@include em((
.demo {
font-size: em.convert($h1-font-size, $base-font-size); // Simple
border-bottom: em.convert(1px solid black, $h1-font-size); // Shorthand
box-shadow: em.convert(0 0 2px #ccc, inset 0 0 5px #eee, $h1-font-size); // Multiple values
// Multiple properties
@include em.convert((
margin: 20px 5%,
padding: 10px
), $h1-font-size);
}
```

That will output :
Will output :

```css
h1 {
.demo {
font-size: 1.5em;
border-bottom: 0.04167em solid black;
box-shadow: 0 0 0.08333em #ccc, inset 0 0 0.20833em #eee;
margin: 0.83333em 5%;
padding: 0.41667em;
border-bottom: 0.0416666667em solid black;
box-shadow: 0 0 0.0833333333em #ccc, inset 0 0 0.2083333333em #eee;
margin: 0.8333333333em 5%;
padding: 0.4166666667em;
}
```

## *But it was shorter before!*

It was.

But You can change the namespace to something shorter and use `em` function and mixin instead of `convert`:

```scss
@use "em" as to; // Because why not?

.demo {
font-size: to.em(24px, 16px);
}
```

Or you can even load the library globally (but beware of conflicts, avoided by the idea of modules):

```scss
@use "em" as *;

.demo {
font-size: em(24px, 16px);
}
```

And if you just don't want to use Sass Modules, you can still use `@import` with `em` function and mixin as before:

```scss
@import "sass-em";

.demo {
font-size: rem(24px);
}
```
56 changes: 32 additions & 24 deletions _em.scss
@@ -1,38 +1,46 @@
// list-separator polyfill by Hugo Giraudel (https://sass-compatibility.github.io/#list_separator_function)
@function em-separator($list) {
@if function-exists("list-separator") == true {
@return list-separator($list);
}
@use "sass:list";
@use "sass:map";
@use "sass:math";
@use "sass:meta";

$test-list: ();
@each $item in $list {
$test-list: append($test-list, $item, space);
// Dart Sass <1.33.0 compatibility
@function _divide($a, $b) {
@if map.has-key(meta.module-functions("math"), "div") {
@return math.div($a, $b);
}

@return if($test-list == $list, space, comma);
@return $a / $b;
}

@function em($values...) {
$context: nth($values, length($values));
@function convert($values...) {
$context: list.nth($values, list.length($values));
$result: ();
$separator: em-separator($values);

@for $i from 1 through length($values) - 1 {
$value: nth($values, $i);
@if type-of($value) == "number" and unit($value) == "px" {
$result: append($result, $value / $context * 1em, $separator);
} @else if type-of($value) == "list" {
$result: append($result, em(append($value, $context)...), $separator);
$separator: list.separator($values);

@for $i from 1 through list.length($values) - 1 {
$value: list.nth($values, $i);
@if meta.type-of($value) == "number" and math.unit($value) == "px" {
$result: list.append($result, _divide($value, $context) * 1em, $separator);
} @else if meta.type-of($value) == "list" {
$result: list.append($result, em(list.append($value, $context)...), $separator);
} @else {
$result: append($result, $value, $separator);
$result: list.append($result, $value, $separator);
}
}

@return if(length($result) == 1, nth($result, 1), $result);
@return if(list.length($result) == 1, list.nth($result, 1), $result);
}

@mixin em($properties, $context) {
@each $property in map-keys($properties) {
#{$property}: em(append(map-get($properties, $property), $context)...);
@mixin convert($properties, $context) {
@each $property in map.keys($properties) {
#{$property}: em(list.append(map.get($properties, $property), $context)...);
}
}

@function em($values...) {
@return convert($values...);
}

@mixin em($properties, $context) {
@include convert($properties, $context);
}
20 changes: 0 additions & 20 deletions bower.json

This file was deleted.

11 changes: 11 additions & 0 deletions index.import.scss
@@ -0,0 +1,11 @@
@use "em";

@forward "em" as em-* hide em-em;

@function em($values...) {
@return em.convert($values...);
}

@mixin em($properties, $context) {
@include em.convert($properties, $context);
}
1 change: 1 addition & 0 deletions index.scss
@@ -0,0 +1 @@
@forward "em";
85 changes: 85 additions & 0 deletions index.test.js
@@ -0,0 +1,85 @@
var sass = require('sass');

function render(data) {
return new Promise(function(resolve, reject) {
sass.render({ data, includePaths: ['./'], precision: 5 }, function(err, data) {
if (err !== null) reject(err);
else resolve(data.css.toString());
});
});
}

async function run(input, output, config = `@use "." as em;`) {
const a = await render(config.concat(input));
const b = await render(output);
expect(a).toEqual(b);
};

it('Simple', () => run(
'.simple { font-size: em.convert(24px, 16px); }',
'.simple { font-size: 1.5em; }'
));

it('Multiple values', () => run(
'.multiple { padding: em.convert(5px 10px, 16px); }',
'.multiple { padding: 0.3125em 0.625em; }'
));

it('Multiple mixed values', () => run(
'.mixed { border-bottom: em.convert(1px solid black, 16px); }',
'.mixed { border-bottom: 0.0625em solid black; }'
));

it('Comma-separated values', () => run(
'.comma { box-shadow: em.convert(0 0 2px #ccc, inset 0 0 5px #eee, 16px); }',
'.comma { box-shadow: 0 0 0.125em #ccc, inset 0 0 0.3125em #eee; }'
));

it('Alternate use', () => run(
'.alternate { text-shadow: em.convert(1px 1px, 16px) #eee, em.convert(-1px, 16px) 0 #eee; }',
'.alternate { text-shadow: 0.0625em 0.0625em #eee, -0.0625em 0 #eee; }',
));

it('Multiple properties', () => run(
'.multiple-properties { @include em.convert((font-size: 24px, margin: 10px 1.5em), 16px); }',
'.multiple-properties { font-size: 1.5em; margin: 0.625em 1.5em; }',
));

it('Changing namespace', () => run(
'.changing-namespace { font-size: to.em(24px, 16px); }',
'.changing-namespace { font-size: 1.5em; }',
'@use "." as to;',
));

it('Global namespace', () => run(
'.global-namespace { font-size: em(24px, 16px); }',
'.global-namespace { font-size: 1.5em; }',
'@use "." as *;',
));

it('Legacy import', () => run(
'.legacy-import { @include em((font-size: 24px), 16px); margin: em(10px 1.5em, 16px); }',
'.legacy-import { font-size: 1.5em; margin: 0.625em 1.5em; }',
'@import ".";',
));

it('Demo', () => run(`$base-font-size: 16px;
$h1-font-size: 24px;
.demo {
font-size: em.convert($h1-font-size, $base-font-size); // Simple
border-bottom: em.convert(1px solid black, $h1-font-size); // Shorthand
box-shadow: em.convert(0 0 2px #ccc, inset 0 0 5px #eee, $h1-font-size); // Multiple values
// Multiple properties
@include em.convert((
margin: 20px 5%,
padding: 10px
), $h1-font-size);
}`,`.demo {
font-size: 1.5em;
border-bottom: 0.0416666667em solid black;
box-shadow: 0 0 0.0833333333em #ccc, inset 0 0 0.2083333333em #eee;
margin: 0.8333333333em 5%;
padding: 0.4166666667em;
}`
));

0 comments on commit 7177cf0

Please sign in to comment.