Anchor Positioning Is Disruptive
New layouts will be possible
The more I play with it, the more convinced I am that anchor positioning is going to unlock some surprising new layouts.
Getting started with @use
and @forward
Dart Sass
(the primary Sass implementation) released version 1.23.0
last night with a major new feature: modules. This is a common
feature in many languages – but for those of us who primarily write CSS,
it can be a big mental shift. Let’s take a look at the basics.
Don’t worry if it doesn’t make sense right away: this version is completely backwards compatible, and we have at least a year before anything will be deprecated from the old way of doing things.
During that transition period:
I’ll be posting a more in-depth article to CSS-Tricks on Monday, but I wanted to get a quick primer out right away.
Update: the full article is now live at CSS-Tricks
One of the most popular Sass features is the ability to split your CSS code into multiple smaller files, and then merge them back together into a single (often minified) CSS document for the browser.
As our projects get bigger, that becomes even more important – but the
relationships between files can also get blurry or confusing. We might
see a variable, and not know where it was defined, then search through
our files and find several definitions. We might want to use an outside
library like Accoutrement, but it includes a color()
function, and
we already have one, so the two would conflict.
Modules are meant to clarify those relationships, both for us as developers and for the compiler. Every file is a “module”, and everything in that module is explicitly defined – including dependencies.
In my old Sass projects, I could do something like this:
// app-styles.scss
// configuration...
$font-path: '../fonts/';
// accoutrement now has access to my configuration...
@import 'accoutrement/sass/tools';
// all these files have access to accoutrement, and each other...
@import 'fonts';
@import 'type';
@import 'layout';
In a module system, that will no longer work each file that we import
would become available inside app-styles.scss
, but they would have no
access to each other or to the variables (like $font-path
) that are
defined locally. If Accoutrement needs access to the $font-path
, then
we’ll have to make that relationship clear. If _fonts.scss
needs
access to Accoutrement, we also need to make that clear.
By making it all explicit, we’re not just adding boilerplate – we’re making it possible to look at a single file and know:
@import
with @use
& @forward
Sass @import
is built on top of CSS @import
– it does the same
things, and then some more things. That distinction isn’t always clear
to users. I have to look it up every time. For example, if we
@import 'fonts.css'
, Sass will treat it as a CSS import and do nothing
–but if we @import 'fonts'
(no extension), Sass will happily import
the file just like any other Scss.
Over the next few years, Sass features of @import
will be removed
entirely. We’re giving that syntax back to CSS, and building modules
with an entirely new syntax. During the transition, the two syntaxes
will work together smoothly:
@import
a file written with @use
/@forward
syntax@use
or @forward
a file written with @import
syntaxWhile they work together, I highly recommend learning the new system, and using it for new code.
@use
modules where you need themSince the module system is all about explicit naming (and namespacing)
of dependencies, We’ll often need one or more @use
statements at the
top of Sass documents. If we want access to Accoutrement in a file, we
can @use
Accoutrement:
@use 'accoutrement/sass/tools';
Like @import
, that makes Accoutrement tools available in our file!
Unlike @import
…
@use
after itA “namespace” works like a prefix. The default namespace is based on the
end of the import path, so accoutrement/sass/tools
will create a
tools
namespace. Namespaced “members” can be accessed with that prefix
separated by a period: <namespace>.$variable
,
<namespace>.function()
, or <namespace>.mixin()
:
// The default namespace will be "tools"
@use 'accoutrement/sass/tools';
// The Accoutrement "animate()" mixin
@include tools.animate('fade-in');
// The Accoutrement "color()" function
html { color: tools.color('brand-primary'); }
// The Accoutrement "$font-path" variable
$my-font-url: tools.$font-path + 'my-font.woff2';
We can change that namespace by adding as <name>
to the @use
statement:
@use 'accoutrement/sass/tools' as amt;
@include amt.animate('fade-in');
We can even use as *
to make external members available without a
namespace – but I don’t recommend it very often. Namespaces are a good
thing.
(Note that as *
does not make members “global” in a project-wide
sense, it just removes the local namespace.)
@forward
modules to combine themI’ll often group small Sass files together in a directory like
layout/
, and then merge them all together in a file called
layout/_index.scss
– so I can import them all at once:
// layout/_index.scss
@import 'banner';
@import 'nav';
@import 'main';
@import 'footer';
// app-styles.scss
@import 'layout'; // sass knows to grab the index file...
The new module system has a special syntax for that: passing along other files as though they are all part of one module.
// layout/_index.scss
@forward 'banner';
@forward 'nav';
@forward 'main';
@forward 'footer';
// app-styles.scss
@use 'layout';
Since Accoutrement (in our example) has no access to local variables, we
need a way to explicitly configure the library before we @use
it. In
brief, module configuration looks like this:
@use 'accoutrement/sass/tools' with (
$font-path: '../fonts/',
/* additional config variables as needed */
);
It’s like a Sass map, but with $
on all the key names to make it clear
they are variables.
This is where things can get the most confusing, because
@use
a moduleThis will take some getting used to, and can be difficult to debug – but
there are a few patterns you can use. Either put configurations at the
very top of your “entrypoint” (the main file that imports everything
else), or combine @use
with @forward
to create a wrapper around the
configured library, and forward the results with optional extensions:
// _tools.scss
@use 'accoutrement/sass/tools' with (
$font-path: '../fonts/',
);
@forward 'accoutrement/sass/tools';
// add extensions here, as desired
Now Accoutrement has been used and configured and forwarded all in one
place. In all our other files, we can @use 'tools'
to access the
fully-configured Accoutrement without any danger of duplicate or
out-of-order configurations:
// _banner.scss
@use 'tools';
// _nav.scss
@use 'tools';
// etc...
There is much more to cover – from Sass core modules (e.g.
@use 'sass:math'
) to import-only files, public and private members,
the load-css()
mixin, and so on. I cover all of that in the article
for CSS-Tricks.
If you want to get a head start, the amazing new documentation has a lot of helpful detail, and Natalie has posted an overview on the Sass Blog. I’ve also created a library called Cascading Color Systems, and a new website for Teacup Gorilla that both use the module system. Both projects are very much under construction, but feel free to dig around.
Check back soon for more details – and if you have questions, feel free to reach out.
New layouts will be possible
The more I play with it, the more convinced I am that anchor positioning is going to unlock some surprising new layouts.
Performance, scope, and fallbacks for the anchor positioning polyfill
Our sponsors are supporting the continued development of the CSS Anchor Positioning Polyfill. Here’s a summary of the latest updates.
Are we measuring what we meant to measure?
There’s been a lot of interest in the results of the annual State of CSS survey, but are we asking all the right questions?