From 9bd1148489813fa5eaaa8da07e646a929293dae3 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 20 Aug 2019 18:20:53 -0700 Subject: [PATCH] Document the module system in existing files Documentation for @use and @forward themselves will come in a future PR. Partially addresses #263 --- data/documentation.yml | 2 + .../documentation/at-rules/import.html.md.erb | 128 ++++++++++++++++++ .../documentation/cli/dart-sass.html.md.erb | 12 +- .../documentation/cli/ruby-sass.html.md.erb | 6 +- source/documentation/js-api.html.md.erb | 34 ++--- .../snippets/_module-system-status.erb | 6 + source/documentation/syntax.html.md | 2 +- .../syntax/structure.html.md.erb | 1 + source/documentation/variables.html.md.erb | 39 ++++-- source/guide.html.haml | 94 ++++++------- 10 files changed, 237 insertions(+), 87 deletions(-) create mode 100644 source/documentation/snippets/_module-system-status.erb diff --git a/data/documentation.yml b/data/documentation.yml index 67013fbd2..ea2001dc4 100644 --- a/data/documentation.yml +++ b/data/documentation.yml @@ -14,6 +14,8 @@ toc: - Interpolation: /documentation/interpolation - At-Rules: /documentation/at-rules :children: + - @use: /documentation/at-rules/use + - @forward: /documentation/at-rules/forward - @import: /documentation/at-rules/import - @mixin and @include: /documentation/at-rules/mixin - @function: /documentation/at-rules/function diff --git a/source/documentation/at-rules/import.html.md.erb b/source/documentation/at-rules/import.html.md.erb index 542addc1c..493e96771 100644 --- a/source/documentation/at-rules/import.html.md.erb +++ b/source/documentation/at-rules/import.html.md.erb @@ -18,6 +18,48 @@ required to have quotes. [indented syntax]: ../syntax#the-indented-syntax +<% heads_up do %> + Continued use of the `@import` rule is discouraged. It will be [gradually + phased out][] over the next few years, and eventually removed from the + language entirely. The [`@use` rule][] should be used instead. + + [gradually phased out]: https://github.com/sass/sass/blob/master/accepted/module-system.md#timeline + [`@use` rule]: use + + #### What's Wrong With `@import`? + + The `@import` rule has a number of serious issues: + + * `@import` makes all variables, mixins, and functions globally accessible. + This makes it very difficult for people (or tools) to tell where anything is + defined. + + * Because everything's global, libraries are forced to add prefixes to all + their members to avoid naming conflicts. + + * [`@extend` rules][] are also global, which makes it difficult to predict + which style rules will be extended. + + [`@extend` rules]: extend + + * Each stylesheet is executed and its CSS emitted *every time* it's + `@import`ed, which increases compilation time and produces bloated output. + + * There was no way to define private members or placeholder selectors that + were inaccessible to downstream stylesheets. + + All of these are addressed in `@use` and the new module system. + + #### How Do I Migrate? + + We've written a [migration tool][] that can automatically convert most + `@import`-based code to `@use`-based code in a flash. Just point it at your + entrypoints and let it run! + + [migration tool]: cli/migrator + +<% end %> + <% example do %> // foundation/_code.scss code { @@ -414,3 +456,89 @@ for example based on mixin parameters. @include google-font("Droid Sans") <% end %> + +## Import and Modules + +<%= partial '../snippets/module-system-status' %> + +Sass's [module system][] integrates seamlessly with `@import`, whether you're +importing a file that contains `@use` rules or loading a file that contains +imports as a module. We want to make the transition from `@import` to `@use` as +smooth as possible. + +[module system]: use + +### Importing a Module-System File + +When you import a file that contains `@use` rules, the importing file has access +to all members (even private members) defined directly in that file, but *not* +any members from modules that file has loaded. However, if that file contains +[`@forward` rules][], the importing file will have access to forwarded members. +This means that you can import a library that was written to be used with the +module system. + +[`@forward` rules]: forward + +<% heads_up do %> + When a file with `@use` rules is imported, all the CSS transitively loaded by + those is included in the resulting stylesheet, even if it's already been + included by another import. If you're not careful, this can result in bloated + CSS output! +<% end %> + +#### Import-Only Files + +An API that makes sense for `@use` might not make sense for `@import`. For +example, `@use` adds a namespace to all members by default so you can safely use +short names, but `@import` doesn't so you might need something longer. If you're +a library author, you may be concerned that if you update your library to use +the new module system, your existing `@import`-based users will break. + +To make this easier, Sass also supports *import-only files*. If you name a file +`.import.scss`, it will only be loaded for imports, not for `@use`s. This +way, you can retain compatibility for `@import` users while still providing a +nice API for users of the new module system. + +<% example(autogen_css: false) do %> + // _reset.scss + + // Module system users write `@include reset.list()`. + @mixin list() { + ul { + margin: 0; + padding: 0; + list-style: none; + } + } + --- + // _reset.import.scss + + // Legacy import users can keep writing `@include reset-list()`. + @forward "reset" as reset-*; + === + // _reset.sass + + // Module system users write `@include reset.list()`. + @mixin list() + ul + margin: 0 + padding: 0 + list-style: none + --- + // _reset.import.sass + + // Legacy import users can keep writing `@include reset-list()`. + @forward "reset" as reset-* +<% end %> + +### Loading a Module That Contains Imports + +When you use `@use` (or `@forward`) load a module that uses `@import`, that +module will contain all the public members defined by the stylesheet you load +*and* everything that stylesheet transitively imports. In other words, +everything that's imported is treated as though it were written in one big +stylesheet. + +This makes it easy to convert start using `@use` in a stylesheet even before all +the libraries you depend on have converted to the new module system. Be aware, +though, that if they do convert their APIs may well change! diff --git a/source/documentation/cli/dart-sass.html.md.erb b/source/documentation/cli/dart-sass.html.md.erb index 820916126..11961d15b 100644 --- a/source/documentation/cli/dart-sass.html.md.erb +++ b/source/documentation/cli/dart-sass.html.md.erb @@ -91,11 +91,11 @@ The `--stdin` flag may not be used with [many-to-many mode][]. This flag tells Sass to parse the input file as the [indented syntax][]. If it's used in [many-to-many mode][], all input files are parsed as the indented -syntax, although files they [import][] will have their syntax determined as -usual. The inverse, `--no-indented`, can be used to force all input files to be -parsed as [SCSS][] instead. +syntax, although files they [use][] will have their syntax determined as usual. +The inverse, `--no-indented`, can be used to force all input files to be parsed +as [SCSS][] instead. -[import]: ../at-rules/import +[use]: ../at-rules/use The `--indented` flag is mostly useful when the input file is coming from [standard input][], so its syntax can't be automatically determined. @@ -110,10 +110,10 @@ h1 { #### `--load-path` This option (abbreviated `-I`) adds an additional [load path][] for Sass to look -for imports. It can be passed multiple times to provide multiple load paths. +for stylesheets. It can be passed multiple times to provide multiple load paths. Earlier load paths will take precedence over later ones. -[load path]: ../at-rules/import#load-paths +[load path]: ../at-rules/use#load-paths ```shellsession $ sass --load-path=node_modules/bootstrap/dist/css style.scss style.css diff --git a/source/documentation/cli/ruby-sass.html.md.erb b/source/documentation/cli/ruby-sass.html.md.erb index ce27d0c30..0de20b3f2 100644 --- a/source/documentation/cli/ruby-sass.html.md.erb +++ b/source/documentation/cli/ruby-sass.html.md.erb @@ -80,7 +80,7 @@ When compiling whole directories, Sass will ignore [partial files][] whose names begin with `_`. You can use partials to separate out your stylesheets without creating a bunch of unnecessary output files. -[partial files]: ../at-rules/import#partials +[partial files]: ../at-rules/use#partials Many-to-many mode will only compile stylesheets whose dependencies have been modified more recently than the corresponding CSS file was generated. It will @@ -93,10 +93,10 @@ also print status messages when updating stylesheets. #### `--load-path` This option (abbreviated `-I`) adds an additional [load path][] for Sass to look -for imports. It can be passed multiple times to provide multiple load paths. +for stylesheets. It can be passed multiple times to provide multiple load paths. Earlier load paths will take precedence over later ones. -[load path]: ../at-rules/import#load-paths +[load path]: ../at-rules/use#load-paths ```shellsession $ sass --load-path=node_modules/bootstrap/dist/css style.scss style.css diff --git a/source/documentation/js-api.html.md.erb b/source/documentation/js-api.html.md.erb index 9441eb4f2..fd7944629 100644 --- a/source/documentation/js-api.html.md.erb +++ b/source/documentation/js-api.html.md.erb @@ -137,8 +137,8 @@ console.log(result.map.toString()); An array of the absolute paths of all Sass files loaded during compilation. If a stylesheet was loaded from an [importer][] that returned the stylesheet's -contents, the raw strings of that stylesheet's `@import` is included in this -array. +contents, the raw string of the `@use` or `@import` that loaded that stylesheet +is included in this array. [importer]: #importer @@ -188,9 +188,9 @@ provides the same information. The stylesheet where the error occurred. If the error occurred in a stylesheet loaded from disk, this is the absolute path of that stylesheet. If the error occurred in a stylesheet that was loaded from an [importer][] which returned the -stylesheet's contents, this is the raw string of that stylesheet's `@import`. If -it occurred in the contents of the [`data` option][], this is the string -`"stdin"`. +stylesheet's contents, this is the raw string of the `@use` or `@import` that +loaded that stylesheet. If it occurred in the contents of the [`data` option][], +this is the string `"stdin"`. #### `error.line` @@ -741,21 +741,23 @@ h1 { somewhere else to break it. <% end %> -This option defines one or more additional handlers for loading files when an -[`@import` rule][] is encountered. It can either be a single JavaScript -function, or an array of functions. These functions are always passed two -arguments: +This option defines one or more additional handlers for loading files when a +[`@use` rule] or an [`@import` rule][] is encountered. It can either be a single +JavaScript function, or an array of functions. These functions are always passed +two arguments: +[`@use` rule]: at-rules/use [`@import` rule]: at-rules/import -1. The `@import` rule's URL as a string, exactly as it appears in the +1. The `@use` or `@import` rule's URL as a string, exactly as it appears in the stylesheet. -2. A string identifying for the stylesheet that contained the `@import`. This - identifier's format depends on how that stylesheet was loaded: +2. A string identifying for the stylesheet that contained the `@use` or + `@import`. This identifier's format depends on how that stylesheet was + loaded: * If the stylesheet was loaded from the filesystem, it's the absolute path of its file. * If the stylesheet was loaded from an importer that returned its contents, - it's the URL of the `@import` rule that loaded it. + it's the URL of the `@use` or `@import` rule that loaded it. * If the stylesheet came from the [`data` option][], it's the string `"stdin"`. @@ -775,7 +777,7 @@ asynchronously pass the result of the import once it's complete. Imports are resolved by trying, in order: -* Loading a file relative to the file in which the `@import` appeared. +* Loading a file relative to the file in which the `@use` or `@import` appeared. * Each custom importer. @@ -793,7 +795,7 @@ sass.render({ // This importer uses the synchronous API, and can be passed to either // renderSync() or render(). function(url, prev) { - // This generates a stylesheet from scratch for `@import "big-headers"`. + // This generates a stylesheet from scratch for `@use "big-headers"`. if (url != "big-headers") return null; return { @@ -807,7 +809,7 @@ h1 { // This importer uses the asynchronous API, and can only be passed to // render(). function(url, prev, done) { - // Convert `@import "foo/bar"` to "node_modules/foo/sass/bar". + // Convert `@use "foo/bar"` to "node_modules/foo/sass/bar". var components = url.split('/'); var innerPath = components.slice(1).join('/'); done({ diff --git a/source/documentation/snippets/_module-system-status.erb b/source/documentation/snippets/_module-system-status.erb new file mode 100644 index 000000000..45073ecc2 --- /dev/null +++ b/source/documentation/snippets/_module-system-status.erb @@ -0,0 +1,6 @@ +<% impl_status dart: '(unreleased)', libsass: false, ruby: false do %> + Only Dart Sass currently supports `@use`. Users of other implementations must + use the [`@import` rule][] instead. + + [`@import` rule]: /documentation/at-rules/import +<% end %> diff --git a/source/documentation/syntax.html.md b/source/documentation/syntax.html.md index 2e686adcc..84540b952 100644 --- a/source/documentation/syntax.html.md +++ b/source/documentation/syntax.html.md @@ -1,7 +1,7 @@ --- title: Syntax introduction: > - Sass supports two different syntaxes. Each one can import the other, so it's + Sass supports two different syntaxes. Each one can load the other, so it's up to you and your team which one to choose. --- diff --git a/source/documentation/syntax/structure.html.md.erb b/source/documentation/syntax/structure.html.md.erb index d5810ec46..f92361361 100644 --- a/source/documentation/syntax/structure.html.md.erb +++ b/source/documentation/syntax/structure.html.md.erb @@ -43,6 +43,7 @@ These statements produce CSS. They can be used anywhere except within a These statements can only be used at the top level of a stylesheet, or nested within a CSS statement at the top level: +* [Module loads](../at-rules/use), using `@use`. * [Imports](../at-rules/import), using `@import`. * [Mixin definitions](../at-rules/mixin) using `@mixin`. * [Function definitions](../at-rules/function) using `@function`. diff --git a/source/documentation/variables.html.md.erb b/source/documentation/variables.html.md.erb index bf0e2ee91..a7d53c810 100644 --- a/source/documentation/variables.html.md.erb +++ b/source/documentation/variables.html.md.erb @@ -88,11 +88,25 @@ use them to generate CSS. To make this possible, Sass provides the `!default` flag. This assigns a value to a variable *only if* that variable isn't defined or its value is [`null`][]. -Otherwise, the existing value will be used. This way, users can set variables -before they import your library to customize its behavior. +Otherwise, the existing value will be used. [`null`]: values/null +### Customizing Modules + +<%= partial 'snippets/module-system-status' %> + +Variables defined with `!default` can be customized when loading a module with +the [`@use` rule][]. Sass libraries often use `!default` variables to allow +their users to customize the library's CSS. + +[`@use` rule]: at-rules/use + +To load a module with customization, write `@use with (: +, : )`. The customized values will override the +variables' default values. Only variables written at the top level of the +stylesheet with a `!default` flag can be customized. + <% example do %> // _library.scss $black: #000 !default; @@ -105,10 +119,10 @@ before they import your library to customize its behavior. } --- // style.scss - $black: #222; - $border-radius: 0.1rem; - - @import 'library'; + @use 'library' with ( + $black: #222, + $border-radius: 0.1rem + ); === // _library.sass $black: #000 !default @@ -120,10 +134,7 @@ before they import your library to customize its behavior. box-shadow: $box-shadow --- // style.sass - $black: #222 - $border-radius: 0.1rem - - @import 'library' + @use 'library' with ($black: #222, $border-radius: 0.1rem) === code { border-radius: 0.1rem; @@ -134,10 +145,10 @@ before they import your library to customize its behavior. ## Scope Variables declared at the top level of a stylesheet are *global*. This means -that they can be accessed anywhere after they've been declared—even in another -stylesheet! But that's not true for all variables. Those declared in blocks -(curly braces in SCSS or indented code in Sass) are usually *local*, and can -only be accessed within the block they were declared. +that they can be accessed anywhere in their module after they've been declared. +But that's not true for all variables. Those declared in blocks (curly braces in +SCSS or indented code in Sass) are usually *local*, and can only be accessed +within the block they were declared. <% example do %> $global-variable: global value; diff --git a/source/guide.html.haml b/source/guide.html.haml index 2eda441c3..3a5e1b86d 100644 --- a/source/guide.html.haml +++ b/source/guide.html.haml @@ -15,7 +15,7 @@ introduction: > - [Variables](#topic-2) - [Nesting](#topic-3) - [Partials](#topic-4) - - [Import](#topic-5) + - [Modules](#topic-5) - [Mixins](#topic-6) - [Inheritance](#topic-7) - [Operators](#topic-8) @@ -158,72 +158,72 @@ introduction: > Sass file named with a leading underscore. You might name it something like `_partial.scss`. The underscore lets Sass know that the file is only a partial file and that it should not be generated into a CSS file. Sass - partials are used with the `@import` directive. + partials are used with the `@use` rule. --- %section#topic-5 - :markdown - ## Import + %h2 Modules + = partial 'documentation/snippets/module-system-status' - CSS has an import option that lets you split your CSS into smaller, more - maintainable portions. The only drawback is that each time you use - `@import` in CSS it creates another HTTP request. Sass builds on top of - the current CSS `@import` but instead of requiring an HTTP request, Sass - will take the file that you want to import and combine it with the file - you're importing into so you can serve a single CSS file to the web browser. + :markdown + You don't have to write all your Sass in a single file. You can split it up + however you want with the `@use` rule. This rule loads another Sass file as + a *module*, which means you can refer to its variables, [mixins][], and + [functions][] in your Sass file with a namespace based on the filename. + Using a file will also include the CSS it generates in your compiled output! - Let's say you have a couple of Sass files, `_reset.scss` and `base.scss`. - We want to import `_reset.scss` into `base.scss`. + [mixins]: #topic-6 + [functions]: documentation/at-rules/function - example do :plain - // _reset.scss - html, - body, - ul, - ol { - margin: 0; - padding: 0; + // _base.scss + $font-stack: Helvetica, sans-serif; + $primary-color: #333; + + body { + font: 100% $font-stack; + color: $primary-color; } --- - // base.scss - @import 'reset'; - body { - font: 100% Helvetica, sans-serif; - background-color: #efefef; + // styles.scss + @use 'base'; + + .inverse { + background-color: base.$primary-color; + color: white; } === - // _reset.sass - html, - body, - ul, - ol - margin: 0 - padding: 0 - --- - // base.sass - @import reset + // _base.sass + $font-stack: Helvetica, sans-serif + $primary-color: #333 + body - font: 100% Helvetica, sans-serif - background-color: #efefef + font: 100% $font-stack + color: $primary-color + --- + // styles.sass + @use 'base' + + .inverse + background-color: base.$primary-color + color: white === - html, - body, - ul, - ol { - margin: 0; - padding: 0; - } body { font: 100% Helvetica, sans-serif; - background-color: #efefef; + color: #333; + } + + .inverse { + background-color: #333; + color: white; } :markdown - Notice we're using `@import 'reset';` in the `base.scss` file. When you - import a file you don't need to include the file extension `.scss`. Sass - is smart and will figure it out for you. + Notice we're using `@use 'reset';` in the `base.scss` file. When you use a + file you don't need to include the file extension. Sass is smart and will + figure it out for you. ---