CSS mixins

Do you really need SASS?

Mixins without preprocessor

CSS preprocessors as SASS have become an important step in the front-end landscape to simplify stylesheets creation and maintening. However they have lost a bit of prominance since the apparition of the new CSS rising star: Custom properties AKA CSS variables. And JAMstack static site generators as Eleventy could be another factor of preprocessors darkening because they give you all the bolts you need to create the elements you want to use and re-use with pure CSS and Vanilla JavaScript.

11tyTips contains an example of a pseudo-CSS file which, preprocessed by the Nunjucks template engine, produces a genuine CSS file with a few repeats of the same @font_face CSS rule pattern only three repeats because 11tyTips is very thrifty regarding the number of fonts beeing used .

11tyTips/source/matrix/assets/styles/css/parts/_font_face_mixin_.css
Prism

/* defaults: styles: 'Regular', type_s: 'woff2' */
{{
  [
    { family_s:'Harmattan', file_s: 'harmattan-v5-latin-regular' },
    { family_s:'Athiti',    file_s: 'athiti-v3-latin-regular' },
    { family_s:'Fira Code', file_s: 'FiraCode-Regular' }
  ] | font_face( U_o.url_s )
}}

But where is CSS in this .css file? Nowhere actually: the CSS fragment is created by the requested filter called font_face. And, apart the opening and closing double braces {{ ... }} pointing out the declaration of a JavaScript variable , everything is a JavaScript Array syntax declaration, an Array to be processed by that font_face filter.

Once again, the substitution is made by a magical Nunjucks filter registered at configuration step in the 11ty directory filters.js file.

11tyTips/source/make/11ty/filters.js
Prism

//...
  const MIXIN_o = require('../lib/css_mixin.js')
  make_o.addFilter('font_face', ( face_a, ...args_ ) => MIXIN_o.font_face__s( face_a, ...args_ ) )
//...

The face_a argument of this filter is the Array declared in the previous _font_face_mixin_.css file:

11tyTips/source/make/lib/css_mixin.js
Prism

module.exports =
{
  font_face__s: ( face_a, ...args_ ) =>
  {
    if ( !face_a || !args_ ) return ''
    let code_s = ''
    face_a.forEach( face_o =>
      {
        const family_s = face_o.family_s
        const file_s   = face_o.file_s
        const style_s  = face_o.style_s || 'Regular'
        const type_s   = face_o.type_s || 'woff2'
        code_s += `
@font-face
{
  font-display: swap;
  font-family: '${family_s}';
  font-style: normal;
  font-weight: 400;
  src:
    local('${family_s}'),
    local('${family_s}-${style_s}'),
    url('${args_[0]}assets/fonts/${file_s}.${type_s}')
    format('${type_s}');
}`
      }
    )
    return code_s
  },

}

The output of this CSS-like preprocessing is inlined for performance concerns in the page head section thru the _font_note_.njk template. Of course this is only available if you put a "dataTemplateEngine": "njk" property inside your Eleventy configuration file see eleventy configure page .

Data template engine processing, offered not only for Nunjucks but other templating systems, is a major strengh of Eleventy static site generator relevant not only for CSS preprocessing but also for JavaScript files preprocessing. Hence some 11tyTips JS files make use of global data variables or constants as emphasized in the following examples :

const idb_o = new KVIdb( ‘{{A_o.ID_s}}_idb’, ‘{{A_o.ID_s}}_store’ )

const key_s = location_s.slice( location_s.lastIndexOf( ‘{{A_o.COLLECTION_s}}s/’), -extension_n )

But there’s more to explore there…

Comments