question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How should libraries be configured?

See original GitHub issue

Many Sass libraries expose variables that control their styles. Today that’s often accomplished with !default variables that downstream users override before importing the library in question. Because the new module system won’t have global variable scope, we need a new solution for this use-case.

The solution proposed in Draft 2 piggybacks on the concept of module mixins. When a module mixin is used, it can be passed parameters that override !default variables in the module being included. However, this requires any downstream user who want to customize a module to do the multi-step dance of using that module as a mixin and then including that mixin. This is particularly annoying when a user wants to add a customization to an existing @use.

Design Goals

I’d like to find a better solution, but there are a number of conflicting design goals at play:

  1. The common case of configuring a few variables on an upstream library should be easy. This is not well-supported by the Draft 2 behavior.

  2. It should be possible to configure a module using the full power of SassScript.

  3. Locality should hold; separate stylesheets that don’t refer to one another shouldn’t affect one another.

  4. Each module should only be loaded once. This is not well-supported by the Draft 2 behavior.

  5. Existing libraries that use !default should be usable with the new module system.

It’s impossible to satisfy all these goals at once. For example, goal 3 is only satisfied if independent stylesheets can use the same module with different configurations, but that means it would need to be loaded multiple times, violating goal 4. We must find a compromise that works best for our users.

Open Questions

  1. How important is backwards-compatibility, really?

    We could, hypothetically, provide no mechanism to configure modules’ !default variables. This violates goal 5 to such a degree that I suspect it’s impossible, but it would make everything else substantially easier so I think it’s worth mentioning. Other programming languages generally don’t do configuration before a module is loaded; the module exposes an API that’s configured as part of its invocation. We could push Sass libraries to do the same by exposing mixins that are invoked after configuration variables are set, or that just take configuration as arguments.

    Unfortunately, Sass libraries tend to have one big side effect when loaded: they produce CSS output. This would only work if libraries were willing and able to ensure they generated no CSS outside of mixins. This is generally a good practice, and one we’re talking about enforcing internally at Google, but making it a requirement to migrate to the module system seems likely to severely delay that migration.

  2. How often do we expect multiple different configurations of the same module to be used in practice?

    In other words, when deciding between goal 3 and goal 4, which should win? Can we get away with simply forbidding a module to be used multiple times with different configurations within a single Sass compilation?

    Locality makes reasoning about styles easier for the user, but in this case it could also produce confusing behavior when the same module is included with multiple different configurations. CSS inherently has non-local behavior for style rules, and if users aren’t careful about scoping each use of the module separately, they could interfere in confusing and hard-to-debug ways.

    Forbidding different configurations for the same module has some infrastructural benefits as well. It allows implementations (and the spec) to track only a single version of each module, which may or may not have configuration applied. It’s also the safer option in the sense that it would be much easier to add support for multiple configurations later than it would be to take that support away.

    On the other hand, I can certainly imagine advanced users wanting to include multiple different configurations of the same module in different selector or media contexts. If that is a relevant use-case, users are probably doing it today, and failing to support that in the new module system could block their ability to adopt it.

  3. Should users be able to write code before @use?

    To accommodate goal 1, configuration should be passed to a module as part of its @use statement. To accommodate goal 2, users should be able to customize a module’s variables using variable references and even function calls. However, Draft 2 says that

    @use directives […] must come before any directives other than @charset or @forward. Because each @use directive affects the namespace of the entire source file that contains it, whereas most other Sass constructs are purely imperative, keeping it at the top of the file helps reduce confusion.

    Goal 2 would mean making some @use directives interact with imperative Sass features, which means they’d need to be able to be written after variable declarations, and probably control rules and @function rules as well. We could still require that @includes and style rules appear after the first @use, though.

    This could be abused to cause some strange behavior, though. You could write something like

    @use "foo";
    $foo.var: value;
    @use "bar";
    

    and if bar also uses foo, it could hypothetically produce different CSS than it would otherwise. This would make parallelizing module loads difficult, which in turn could affect modular compilation. That said, we could work around this downside by simply forbidding assignments to namespaced variables until after the final @use.

Straw Man

Here’s an example of how this could work, assuming we decide to support !default and allow some code before @use:

$base-color: #abc;
@use "library" with (
  $base-color: #abc,
  $secondary-color: darken(#abc, 10%),
);

Note that this this is adopting the same with syntax that’s been proposed in https://github.com/sass/sass/issues/871#issuecomment-22420943.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:9 (8 by maintainers)

github_iconTop GitHub Comments

2reactions
nex3commented, Oct 9, 2018

Yeah, using map syntax there seems strange when we refer to variables using the $ prefix elsewhere in the module system syntax. Also, one important thing to keep in mind is that load-css() will probably be used much less frequently than @use, so having it be a little strange isn’t the end of the world.

1reaction
nex3commented, Oct 19, 2018

How would this work when using the new @use syntax? Would it be something like:

@use "kickoff-grid" with (
  $layout-max-width: 1200
);

Which would then pass through that value into the grid module?

That’s right. From that point forward, the $layout-max-width variable in the kickoff-grid module would be 1200, for all files that refer to it.

Also, as a side question – the draft mentions Because variables are no longer global with @use – does this mean that there is no concept of global variables at all when switching to @use?

There are no global variables in the sense of variables that are visible at all times in all files, the way top-level variables are with @import. They’re replaced by module variables, which are only visible to files that use the modules they contain. However, module variables are global in the sense that they have the same value from anywhere they’re accessed.

I only ask as I currently use globally scoped variables for theming when the aim is to generate two differently themed stylesheets - so setting a $theme variable before importing dependencies allows overriding of colours and styles. If this is the case – would be interesting to discuss how this would work – would the intention be to use with to pass around the $theme variable to each module as they are called?

If the $theme variable is defined in a module named theme, you could write this at the top of your entrypoint file:

@use "theme" with (
  $theme: "dark"
);

and then every other file that uses theme would see the version with $theme set to "dark".

Read more comments on GitHub >

github_iconTop Results From Across the Web

Setting up a Library: Home - ALA LibGuides
This guide will provide an overview of resources common to all types of libraries, along with some references for some specific situations.
Read more >
Configuring the Institution and Its Libraries
You manage general information about institutions and libraries on the pages at Configuration Menu > General > Libraries > Add a Library or...
Read more >
Configure Libraries | Inventor 2021
To configure Content Center Libraries for use in a project, open Autodesk Inventor Routed Systems and click Manage Projects.
Read more >
Configure Content Center Libraries - YouTube
Your browser can 't play this video. Learn more. Switch camera.
Read more >
Configure JavaScript libraries | WebStorm Documentation
In context of the language and the IDE, a library is a file or a set of files. Functions and methods of these...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found