State of CSS in Meteor

I’m a fan of micro libraries of sort. Instead of hoping for one framework to do everything (especially, if there’s basically one guy trying to maintain that everything), I’d rather use another lib that’s solely focused on the task at hand. It’s a line drawn in sand of course, which components to include in a framwork and which to exclude… I think it’s great the community can & has provided additional components for SUI, such as the date picker!

When a framework tries to include everything, this kind of things tend to happen:

I wanted to use the form styling of Semantic-UI, but to handle the validation with ViewModel’s validation tools. If I include the form-element in my SUI build, it automatically includes a lot of form related JS in the SUI bundle, which I don’t need. That can easily go unnoticed, until you find out your JS bundle is 3Mb in size.

I guess the lesson that could be learnt here is, that with a huge framework such as SUI it is essential to provide the different elements, options, variations as opt-in rather than forced upon. Now it’s a lot of trouble trying to get rid of stuff that’s not needed, like form validations, popups, all the 15 different color themes for everything etc…

It is very slow, as it’s basically rebuilding SUI every time you change any file on the client. At least it used to, I don’t know with the latest speed improvements in the build tool or possible improvements in the package. Especially if you’re not making any changes to SUI, you could get huge performance improvements by creating a separate project for SUI that builds the .css and .js bundles for you to include in a Meteor package.

Setting up your package was a breeze, though I had to look up in the Wiki and the example repo to get a nice copy and paste the configs -experience :slight_smile:

One issue using node-sass, on every style change I get this in my console:

WARNING: version mismatch for node-sass; installed version is 4.4.0, but version 3.x is required by nathantreid:css-modules)

yet the package seems to be working just fine with node-sass@4.4.0

So far this is just awesome. Now I feel like a complete idiot for not switching earlier. Congrats on a great CSS package for Meteor!

Should admit SUI isn’t as modular and easy tool as ant/material frameworks but in terms of style… #onelove
On your side here! Most freedom i’ve seen among css frameworks though khm-lifecycle-khm

@nathantreid, I suppose this is not possible?

// /globals/fonts.m.css
$comicSans: 'Comic Sans';

$headerFont: $comicSans;
$buttonFont: $comicSans;
// package.json
"cssModules": {
    "extensions": [
      "m.css"
    ],
    "enableSassCompilation": [
      "m.css"
    ],
    "postcssPlugins": {
      "postcss-simple-vars": {
        "fileOptions": [
          "globals/fonts.m.css"
        ]
      },
      "postcss-modules-values": {},
      "postcss-nested": {},
      "postcss-modules-local-by-default": {},
      "postcss-modules-extract-imports": {},
      "postcss-modules-scope": {},
      "autoprefixer": {}
    }
  },
// myComponent.html
<template name="myComponent">
  <h1 class="{{styles.header}}">Header</h1>
  <button class="{{styles.button}}">Button</button>
</template>
// myComponent.m.css
.button {
 font-family: $buttonFont;
}

.header {
  font-family: $headerFont;
}

Actually this is possible, but I need to add it to the documentation - sorry about that!
Since you’re using SASS, the variables are interpreted by SASS instead of PostCSS - use the globalVariables option instead:

"globalVariables": [
  "globals/fonts.m.css"
],

The reason for that message is that I haven’t tested node-sass version 4 yet, and the major version bump signifies potentially breaking changes. I don’t want users to get stuck if node-sass changes their library in a way that prevents my package from working with it. I’ll think about adjusting the message and think about the frequency, though.

Thanks, awesome! I was about start filing a pull request to fix an error in the docs regarding where those globals should be defined, but apparently they just work differently based on the key in configs.

I’m still trying to orient myself to this new paradigm, and one thing I find confusing is triggering animations with classes.

Say I used to do something like this (the class is assigned by a ViewModel binding based on the value of isBacksideVisible )

// html
<div {{b "class: { flip: isBacksideVisible }"}} class="card">
  <div class="front">
     <img src="nicepic.jpg"/>
  </div>
  <div class="back">
    <p>it was a cat pic</p>
  </div>
</div>

// card.scss

.card {
   &.flip {
     .front {
        transform: rotateY(180deg);
     }
     .back {
        transform: rotateY(0deg);
     }
   }
   ...
}

How would I accomplish this in the most elegant possible way using CSS Modules, Blaze (and possibly ViewModel if you’re comfortable with that)?

My initial solution:

// card.html
<div class="{{styles.card}} {{#if isBacksideVisible}}{{styles.flip}}{{/if}}">
  <div class="{{styles.front}}">
  ...
</div>

// card.m.css
.card {
  ...
}
.flip {
  .front {
    transform: rotateY(180deg);
  }

  .back {
    transform: rotateY(0deg);
  }
}

My first impression on using this package is “wow”, but I also feel that polished documentation and more examples would make it so much easier to adopt!

I tried this, and this type of error appears in my console:

=> Started proxy.                             
=> Errors prevented startup:                  
   
   While processing files with nathantreid:css-modules (for target
   web.browser):
   native: Cannot convert undefined or null to object
   at hasOwnProperty (native)
   at _has
   (/Users/arggh/.meteor/packages/nathantreid_css-modules/.2.3.1.v8aqot++os+web.browser+web.cordova/plugin.mss.os/npm/node_modules/meteor/mss/node_modules/ramda/dist/ramda.js:174:48)
   at mergeWithKey
   (/Users/arggh/.meteor/packages/nathantreid_css-modules/.2.3.1.v8aqot++os+web.browser+web.cordova/plugin.mss.os/npm/node_modules/meteor/mss/node_modules/ramda/dist/ramda.js:2691:29)
   at f3
   (/Users/arggh/.meteor/packages/nathantreid_css-modules/.2.3.1.v8aqot++os+web.browser+web.cordova/plugin.mss.os/npm/node_modules/meteor/mss/node_modules/ramda/dist/ramda.js:477:22)
   at mergeWith
   (/Users/arggh/.meteor/packages/nathantreid_css-modules/.2.3.1.v8aqot++os+web.browser+web.cordova/plugin.mss.os/npm/node_modules/meteor/mss/node_modules/ramda/dist/ramda.js:5891:16)
   at
   /Users/arggh/.meteor/packages/nathantreid_css-modules/.2.3.1.v8aqot++os+web.browser+web.cordova/plugin.mss.os/npm/node_modules/meteor/mss/node_modules/ramda/dist/ramda.js:454:28
   at Object.f2
   (/Users/arggh/.meteor/packages/nathantreid_css-modules/.2.3.1.v8aqot++os+web.browser+web.cordova/plugin.mss.os/npm/node_modules/meteor/mss/node_modules/ramda/dist/ramda.js:434:22)
   at packages/mss/postcss-plugins.js:57:30
   at forEach
   (/Users/arggh/.meteor/packages/nathantreid_css-modules/.2.3.1.v8aqot++os+web.browser+web.cordova/plugin.mss.os/npm/node_modules/meteor/mss/node_modules/ramda/dist/ramda.js:1874:13)
   at
   /Users/arggh/.meteor/packages/nathantreid_css-modules/.2.3.1.v8aqot++os+web.browser+web.cordova/plugin.mss.os/npm/node_modules/meteor/mss/node_modules/ramda/dist/ramda.js:390:80
   at Object.f2
   (/Users/arggh/.meteor/packages/nathantreid_css-modules/.2.3.1.v8aqot++os+web.browser+web.cordova/plugin.mss.os/npm/node_modules/meteor/mss/node_modules/ramda/dist/ramda.js:434:22)
   at loadPlugins (packages/mss/postcss-plugins.js:50:5)arggh
   at new CssModulesProcessor (packages/mss/css-modules-processor.js:27:27)
   at CssModulesBuildPlugin.processFilesForTarget
   (packages/mss/css-modules-build-plugin.js:54:32)
   
   
=> Your application has errors. Waiting for file change.

My config is here:

"cssModules": {
    "globalVariables": [
      "client/styles/global/fonts.m.css",
      "client/styles/global/colors.m.css",
      "client/styles/global/variables.m.css"
    ],
    "extensions": [
      "m.css"
    ],
    "enableSassCompilation": [
      "m.css"
    ],
    "postcssPlugins": {
      "postcss-simple-vars": {
        "fileOptions": [
          
        ]
      },
      "postcss-modules-values": {},
      "postcss-nested": {},
      "postcss-modules-local-by-default": {},
      "postcss-modules-extract-imports": {},
      "postcss-modules-scope": {},
      "autoprefixer": {}
    }
  },

I’ll respond to both of these later today.

1 Like

With the same config as above, except the globals defined in fileOptions instead, this doesn’t work as I thought it would:

// /client/styles/shared/input.m.css
.a {
  composes: b from ('./utils.m.css');
}

// /client/styles/shared/utils.m.css
.b {
  font-weight: bold;
}

I get this error:

Processing Step: CSS Modules / PostCSS compilation
Unable to compile /Users/arggh/Development/modulesapp/client/styles/shared/input.m.css
CssSyntaxError: postcss-modules-scope: /Users/arggh/Development/modulesapp/client/styles/shared/input.m.css:2:3: referenced class name "b" in composes not found
.a {
  composes: b from ('./utils.m.css');
  ^
}

/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This was a handy starter-kit: https://cdn.rawgit.com/joeybaker/styleguide-css/a2a7f31c/decks/CSS-Modules.html#1

:slight_smile:

I’ve filed an issue for the Cannot convert undefined or null to object error.
The issue is occurring because you are using postcss-simple-vars with the globalVariables option and without specifying any configuration for the postcss-simple-vars plugin. That’s a scenario I hadn’t anticipated. :slight_smile:
For now you can workaround the issue by supplying a fileOption to postcss-simple-vars; even an empty file will do:

"postcss-simple-vars": {
  "fileOptions": [
    "client/empty.txt"
  ]
}

If you’re still using SASS but specifying the global variable in the postcss-simply-vars configuration instead of the globalVariables field, I’m surprised it’s getting that far. I’d have expected it to error out when node-sass ran.
It sounds like PostCSS is experiencing issue compile utils.m.css, but isn’t spitting out an error for that. Can you create an issue on Github and if possible supply a reproduction repository?

I’ll have to push the animation example off to tomorrow since work ran late tonight. I do agree on the documentation / samples, though. I’ve started work on a non-React SCSS example. It’s very basic right now, though - just a conversion of my basic CSS demo: https://github.com/nathantreid/meteor-css-modules-demo-scss

Thank you, adding an empty file (or actually just one of the files used in globalVariables) fixed the error.

I’m not entirely sure how, but soon after I got the composing to work as well.

I created an issue to Github with my thoughts on the process of adopting CSS Modules:

I ended up doing it with a helper like this:

import styles from './card.css';

Template.view.helpers({
   cardStyle() {
      return flipped ? styles.flippedCard : styles.card;
   }
});
  <div class="{{cardStyle}}">
   ...
.card {
   ...
}

.flippedCard {
  composes: card;
  transform: rotateY(180deg);
}

It’s ok I guess, though I’m keen to see a nicer way :slight_smile:

Actually, I just realized that using CSS Modules, I can get rid of Semantic-UI, but keep the natural language with composes and achieve really atomic and reusable CSS like this:

.container {
   composes: left-aligned very-padded four-column grid from "../layouts.m.css";
}

Super excited! :heart_eyes:

Edit: Also, properly isolated :tada: so no more class clashes, which were almost certain if you tried to combine Semantic-UI with any custom or 3rd party CSS.

@jlukic , I guess you can already consider your “goal” with Semantic-UI accomplished in a sense :slight_smile: :point_down:

[quote=“arggh”]Actually, I just realized that using CSS Modules, I can get rid of Semantic-UI, but keep the natural language with composes and achieve really atomic and reusable CSS like this:
[/quote]

That’s pretty much how I would have done it, and I LOVE composition especially for pulling styles from frameworks like bootstrap as well as reusable application-wide styles like you posted. The only thing I’d have added to what you posted is the caveat that although this seems to be the official CSS modules way (one “class” at a time per element) I don’t adhere to this when I start to accumulate lots of variations on a particular element. For example, if I need to flip the card, or make it purple, or both:

.card {
   ...
}

.flippedCard {
  composes: card;
  transform: rotateY(180deg);
}

.purpleCard {
 composes: card;
 background-color: purple;
}

.flippedAndPurpleCard {
 composes: flippedCard;
 composes: purpleCard;
}

About the time I get to flippedAndPurpleCard I start to really think about how many variations there might be, and whether or not I’m better off composing them the CSS Modules way shown above or dynamically in my code (this was all typed directly on the forum, so although it’s based off of actual code it may not be 100% correct):

import styles from './card.css';

Template.view.viewmodel({
   cardStyle() {
     let styles = [styles.card];
     if (this.isPurple())
       styles.push(styles.purple]);
     if (this.isFlipped())
       styles.push(styles.flipped]);
     return styles.join(' ');
   }
});
.card {
   ...
}

.flipped {
  composes: card;
  transform: rotateY(180deg);
}

.purple {
 composes: card;
 background-color: purple;
}

1 Like

@nathantreid, I read from the changelogs that you have implemented some sort of caching.

I’ve experienced rather strange quirks a couple of times. Just now I was trying to build a component using media queries, but the stuff inside the query wouldn’t get picked up in the formed class.

I was already 30 mins into reading how to use media queries with CSS Modules and baffled, tried something, tried something else, went back to original version and bam it suddenly just started working.

Any chance this could have something to do with the caching functionality? How should I manually purge the cache, does meteor reset work?