49

I am making a css grid system that relies on the concept of blocks. So I have a base file like:

$max-columns: 4;
$block-width: 220px;
$block-height: 150px;
$block-margin: 10px;

And it is used by a mixin:

@mixin block ($rows, $columns, $max-columns) {
  display: block;
  float: left;
  margin: $block-margin 0 0 $block-margin;
  box-sizing: border-box;
  width: ($block-width * $columns) - $block-margin;
}

But I'd also like javascript to have access to the variables in the base file. I was thinking that I could make an invisible div, and give it the $block-width, $block-height, and $block-margin attributes and pull the values from there. But max-columns, doesn't map to anything directly, so I'd have to come up with a hacky way to render it in a div. Is there a cleaner way to share values from sass/css to javascript or vice versa?

Jeremy Smith
  • 14,727
  • 19
  • 67
  • 114

10 Answers10

26

If you use webpack you can use sass-loader to exportvariables like:

$animation-length-ms: $animation-length + 0ms;

:export {
  animationMillis: $animation-length-ms;
}

and import them like

import styles from '../styles/animation.scss'    
const millis = parseInt(styles.animationMillis)

https://blog.bluematador.com/posts/how-to-share-variables-between-js-and-sass/

velop
  • 3,102
  • 1
  • 27
  • 30
  • 3
    This! This should be the accepted answer. And I recommend to strip the unit on export like `:export { animationMillis: strip-unit($animation-length-ms);}` with [strip-unit](https://css-tricks.com/snippets/sass/strip-unit-function/) so we can directly use the variable as a number without the need to parse a string. – Martin May 20 '20 at 15:11
  • Perfect sollution! – Marcin Wojtach Apr 19 '21 at 17:13
  • 2
    Does not work in my environment, running `webpack@5.45.1`, `sass-loader@12.1.0` and `node-sass@6.0.1`. The variable `styles` is logged as `undefined`, as such I can't access variables declared in `:export`. Any ideas? – Leon Willens Aug 02 '21 at 09:55
  • 1
    Same here, it was working fine until I update my dependencies. – Julien B. Aug 08 '22 at 03:43
  • @LeonWillens, Julien B any update? – Chandler Bing Apr 08 '23 at 14:37
  • 2
    @ChandlerBing Made some tests with `webpack@5.79.0`, `sass-loader@13.2.2` and `node-sass@8.0.0`. Apparently, it only works if the SCSS files that are `:export`ing values are named like `.modules.scss`, else the import results in an `undefined` value. – Leon Willens Apr 15 '23 at 21:38
14

I consider my solution to be quite hokey; but it does work...

In my _base.scss I have some variables defined:

$menu_bg: rgb(45, 45, 45);
$menu_hover: rgb(0, 0, 0);

In a menu.scss I have:

@import "base";

#jquery_vars {
  .menu_bg {
    background-color: $menu_bg;
  }
  .menu_hover {
    background-color: $menu_hover;
  }
}

And in a handy page template:

<span class="is_hidden" id="jquery_vars">
  <span class="is_hidden menu_bg"></span>
  <span class="is_hidden menu_hover"></span>
</span>

Finally this allows in a nearby jQuery script:

var menu_bg = $('#jquery_vars .menu_bg').css("background-color");
var menu_hover = $('#jquery_vars .menu_hover').css("background-color");

This is so ugly my dad is wearing a bag on his head.

jQuery can pull arbitrary CSS values from page elements; but those elements have to exist. I did try pulling some of these values from raw CSS without creating the spans in the HTML and jQuery came up with undefined. Obviously, if these variables are assigned to "real" objects on your page, you don't really need the arbitrary #jquery_vars element. At the same time, one might forget that .sidebar-left nice-menu li is the vital element being use to feed variables to jQuery.

If someone has anything else, it's got to be cleaner than this...

ericx
  • 835
  • 1
  • 10
  • 18
11

sass-ffi should do the trick, but the opposite way (from JS to SASS/SCSS). It will define a function called ffi-require, which allows you to require .js files from SASS:

config.js:

module.exports = {
     maxColumns: 4,
};

style.scss:

$max-columns: ffi-require('./config', 'maxColumns');

Works with sass-loader (webpack) and node-sass.

futpib
  • 520
  • 6
  • 15
9

You can read the sass file with a server side script, "parse" it and echo the values you need to javascript.

Corubba
  • 2,229
  • 24
  • 30
4

I would like to add that there are now several ways to share data between Sass and JavaScript using JSON. Here are some links to articles detailing various techniques:

It's probably just a matter of time until JSON importing becomes supported natively in Sass.

Teo Dragovic
  • 3,438
  • 20
  • 34
3

I would recommend looking at sass-extract which uses native sass features in order to extract the computed variable values into JSON.

Also if you are using webpack the sass-extract-loader will make it very easy to just require/import the sass files as in const variables = require('sass-extract-loader!./variables.scss'); and have your sass variables in a nice JSON object.

Since it also supports @import statements you can still separate your variables in different files, and no need to add additional preprocessing or separate json files with variables.

There are many alternative ways of accomplishing this as mentioned in other answers, and which one you choose will depend on your use case and environment.

Disclaimer, I am the author of both mentioned libraries.

jgranstrom
  • 576
  • 5
  • 13
1

For ViteJS + React =>

foo.scss => foo.modules.scss

$mobileMax: 720px;

:export {
   mobileMax: #{$mobileMax}
}

Component.jsx

import styles from "./foo.modules.scss";

const { mobileMax } = styles;
Saurabh Nemade
  • 1,522
  • 2
  • 11
  • 20
0

Another way could be to use gulp-template so you can generate any structure you want for your JavaScript.

Sharing Variables between Javascript and Sass using Gulp with gulp-template https://youtu.be/UVeUq8gMYco

It's created from scratch so people could see it from the ground up and there is a git repo with the end result:

https://github.com/PocketNinjaCoUk/shared-js-sass-vars-using-gulp/tree/master/dev

You basically have your config object

saved at ./dev/config.js

module.exports = {
  defaults: {
    colours: {
      primary: '#fc0'
    },
    sizes: {
      small: '100px',
      medium: '500px',
      large: '1000px'
    },
    zIndex: {
      model: 100,
      dropdown: 50,
      header: 10
    }
  }
}

Then you have both of your templates for Sass and Javascript, or less or whatever you want.

Sass underscore template

saved at ./dev/templates/sass-config.txt

<% _.each(defaults, function(category, key) { %>
  // Var <%= key %>
  <% _.each(category, function(value, key) { %>
    $<%= key %>: <%= value %>;
  <% }) %>
<% }) %>

Javascript underscore template

saved at ./dev/templates/js-config.txt

namespace.config = {};

<% _.each(defaults, function(monkey, key) { %>
  namespace.config.<%= key %> = {
  <% i = 1 %>
  <% _.each(monkey, function(value, key) { %>
    <% comma = (Object.keys(monkey).length === i) ? '': ',' %>
    <% if(typeof value === 'string') {%>
      <%= key %>: '<%= value %>'<%= comma %>
    <%} else { %>
      <%= key %> : <%= value %><%= comma %>
    <% } %>
    <% i++ %>
  <% }); %>
  };
<% }) %>

Then the gulp to compile it

var gulp = require('gulp');
var template = require('gulp-template');
var rename = require('gulp-rename');
var removeEmptyLines  = require('gulp-remove-empty-lines');

var sharedVars = require('./dev/config');

gulp.task('compile', function() {
  gulp.src('./dev/templates/sass-config.txt')
    .pipe(template(sharedVars))
    .pipe(rename('_sass-config.scss'))
    .pipe(removeEmptyLines())
    .pipe(gulp.dest('./dev/sass'));

  gulp.src('./dev/templates/js-config.txt')
    .pipe(template(sharedVars))
    .pipe(rename('js-config.js'))
    .pipe(removeEmptyLines())
    .pipe(gulp.dest('./dev/js'));
});
Pocketninja
  • 395
  • 1
  • 12
0

This can be done using gulp-sass-vars-to-js. It generates a .js file from your .scss file. The .js file contains all variables declared in your .scss file. You can then 'require' this generated js into your .js

User52016
  • 684
  • 1
  • 8
  • 16
0

I think it depends on your context. If u are using node solutions like nextjs or vite, u can import javascript inside your style file using node-sass-json-importer.

Lucas Vazquez
  • 1,456
  • 16
  • 20