Using templating and "@use" now Sass has deprecated "@import"

Dart Sass deprecated the use of @import in 1.80.0. Jekyll depends on sass-embedded, ~> 1.75, which looks to track the Dart Sass versioning. Hence, Jekyll now depends on a version of Sass that has deprecated @import.

As mentioned here without much resolution, this means most users of a Jekyll theme will see a lot of deprecation warnings.

Looking into it, it seems to me that @import is intwined with the way Jekyll does templating. That is, Jekyll expects the main scss file to have YAML front matter and can contain Liquid syntax, but the scss partials are pure scss. So typically in Jekyll you would use YAML to configure your main scss file, and then @import the partials. The partials then apply what got configured.

For example, main.scss might contain something like:

$header-image: "{{ site.data.style.header-image }}";
@import "base/page.scss";
@import "components/buttons.scss";
@import "components/navbar.scss";
@import "layout/services.scss";

Each of the partials can then make use of $header-image.

Without @import this doesn’t seem possible. You’d have to @use the partials, which then wouldn’t inherit the variable. And you can’t just @use a separate file that defines the variable, because only the main scss file can use configuration.

I’m new to Jekyll but I understand it’s mature, so if the above is correct (it may not be!), then maybe the way forward is to pin to <1.80.0. Or, given it’s just a warning, ask users to add quiet_deps to their config? I’m not sure if this has to be done by the user, or whether silence_deprecations is a better fit. The latter is not available in jekyll-remote-theme yet (I can’t add link cause newbie, but it’s issue #111).

1 Like

Thank you for bringing this up. My main site is built w/o any theme to avoid dependencies. Another one, built on ā€œminimal-mistakesā€, I just moved to bootstrap, copying all pertinent files to my own assets. A third site, also on ā€œminimal-mistakesā€, which I rarely update, brings up lots of depreciation warnings. Prompted by you, I looked up more about `sass`. My version is 1.97.2, and would hope that the flag `–silence-deprecation` will give it some more years :smiling_face_with_sunglasses:. As you can see, I have no solution. Moving forward, I will just be even more wary of dependencies, though I get it: I maintain a repo myself and strive very hard to avoid breaking changes, but it’s difficult.

1 Like

I looked into this a couple of weeks ago.

I was interested in the ā€˜just-the-docs’ theme, and all the scss uses import and sass variables.

I fiddled around with it and managed to create a version that uses @use, @forward and also mostly css custom variables.

About then, I decided that ā€˜just-the-docs’ wasn’t what I needed, and left it.

It’s sitting in github, in branch sass-update, if you’re interested. WAF (with all faults) as they say in the used book business. Not refined, not squashed, not even properly commented.

Oh very interesting, thanks for sharing.

My understanding is that you could achieve this for the same reason it can be done for minima - the scss file doesn’t have any configuration. There’s no YAML front matter in base.scss, and all the variables.scss are hard coded. So the problem goes away.

But there’s something more going on here - the css files in the _includes folder are liquid files! I’m not sure how this works. They don’t have YAML front matter and I’m not clear on how they get processed. I think it might have something to do with the {% include command in the html includes.

So I think the fundamental issue remains - if you follow the Jekyll method for templating, you’re stuffed. But it’s certainly interesting this seems to be an alternative approach.

Files in _includes/css/*.scss.liquid have frontmatter and liquid includes.

I did find it fiddly tracing all the different locations. Looking at it now I see

  • _includes/css/*.css.liquid
  • _sass/*.scss
  • assets/*.css

Hmm, I still can’t see the frontmatter in those files. However, I realised I shouldn’t anyway, because they’re just the partials! The entry point is actually /assets/css and those files do have the frontmatter, as expected.

So my hypothesis turns out to be false. I said:

the scss file doesn’t have any configuration

but it does (eg. site.color_scheme and site.logo). Now site.color_scheme isn’t inherited (it gets assigned to a liquid variable and then just passed via include. But site.logo is!

So I don’t know how this works:

  1. site.logo is configured in _config.yml :white_check_mark:
  2. Jekyll processes the assets/css/just-the-docs-default.scss file, passing it the site configuration dictionary. :white_check_mark:
  3. That file includes _includes/css/just-the-docs.scss.liquid, which being liquid, also has access to the site dictionary. :white_check_mark:
  4. It assigns the site.logo value to the Sass variable $logo. :white_check_mark:
  5. You’ve changed it to @use _sass/modules.scss and, as is necessary, before $logo gets assigned :red_question_mark:
  6. _sass/modules.scss now @uses _sass/layout.scss :white_check_mark:
  7. Which finally, checks the $logo variable contents. :exclamation_question_mark:

I can’t see how layout.scss can know about the $logo variable. Possibly:

  1. I’m wrong about @use and it does actually pass variables down, even if they’re defined after the @use statement!
  2. There’s some other way layout.scss knows about $logo that I’m not seeing.
  3. Or, it doesn’t, and the port to @use wasn’t entirely successful…

This is super hard to follow and debug, so it could be any of these options! Would love to hear your thoughts.

I hadn’t looked at the logo before. (I did say it was WAF) This is what I’ve found/changed and now pushed:

  1. site.logo is configured in _config.yml (unchanged)

  2. _includes/css/just-the-docs.scss.liquid defines a custom property for the logo (--logo), currently confined to .site-logo, but could be global, or scoped as required.

    {% if site.logo %}
    $logo: ā€œ{{ site.logo | relative_url }}ā€;
    .site-logo {–logo: url({{ site.logo | relative_url }}) }
    {% endif %}
    
  3. _sass/layout.scss uses the property for as a background image. I’ve removed the conditional @if variable-exists(logo), as _includes/title.html only generates div.site-logo if site.logo is true.

    .site-logo {
      width: 100%;
      height: 100%;
      background-image: var(--logo);
      background-repeat: no-repeat;
      background-position: left center;
      background-size: contain;
    }
    

When I define a logo in config.yml I can see the image at the top of the side body.

atb,

Anita

1 Like

Oh interesting! So essentially, to avoid the scoping introduced by Sass @use, you can use CSS Custom Properties, which are globals (or scoped within selectors, which themselves are global). Mercifully you don’t even have to define-before-use, so I think you can @use at the top of the file, as required, and then --variable: {{ site.variable }} below.

I’m not sure whether it’s a forward or a backward step, but it’s a way to get there!

PS. I also noticed you can ā€œConfigure Modulesā€ in Sass. You have to be explicit about which variables you’re configuring, but at least there is some way to pass variables down to files that you @use.

I’m sure you could use modules and scoping in a better way than I have here. I was trying to get something working.

I haven’t written a proper css library, using all the features that could be used, and avoiding globals as far as possible.

My approach is to define colours, sizes and similar basics at :root level:

:root {-color-accent: hsl(190 100% 36%);}

Then for classes or tags set up standard properties (in, for instance ā€˜settings.scss’)

main .content {
  h3 {
    --color: var(--color-accent);
}

then apply these settings directly, or via mixins, to elements.

h3 { color: var(--color); etc... } or

h3 { @include setColor; }

@mixin setColor {
// Could remove the fallbacks and let inheritance take hold
color: var(--color, var(--defaultTextColor);
background-color: var(--backgroundColor, transparent);
}

Replying to myself.

I am always debating myself over the naming of the custom properties.

The examples in my preceding post aren’t necessarily what I’d recommend, and its perhaps one of those endless debates (unix vs VMS only about 40 years ago).

In the preceding post I’m using custom variables named exactly the same as the css attributes. I prefer to use more specifc property name in camelCase.

h3 {
  --textColor: var(--headingColour);
  --backgroundColor: var(--headingBackgroundColor);
}

h3, p, etc {
 color: var(--textColor);
 background-color: var(--backgroundColour);
}



I haven’t even written a bad css library, so you’re well ahead of me. The whole deprecation of @import smells to me of cargo cult aversion to globals, and a strategy not at all suited to the consistency goals and declarative nature of CSS. But I’m sure there’s good rational behind it all and no doubt some CSS projects have got complicated.

Perhaps Jekyll has just lulled people into thinking in terms of configuration being global, permeating CSS files by way of Liquid. Descending to CSS Custom Variables actually seems like a neat way to bypass Sass and apply configuration where it belongs - to basic global variables like your examples.

My interest here is what to do about all the templates and existing sites that relied on the @import behaviour. If there’s a easy path via CSS Custom Variables instead of Sass variables, and a slightly harder path via ā€œConfiguring Modulesā€, then we might have a neat solution.

1 Like

It didn’t take me long to do it manually for jtd.

Converting sass variables to css custom properties is cut/paste so could be done automatically, once you’ve found all the outliers etc.

It is similar when adding @forward and @use, once you have figured out how you will use them.

If you’re a jekyll person, who has an understanding of the templates, it might be even easier.

(I’m tranferring a site to jekyll for the first time),

1 Like