New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A modular CSS proposal #30

Closed
guybedford opened this Issue Apr 22, 2015 · 19 comments

Comments

Projects
None yet
3 participants
@guybedford
Member

guybedford commented Apr 22, 2015

An idea..

component-style.mcss (modular css)

/* $ allows us to define "CSS class name placeholders" */
${base} {
  font-size: 16px;
  color: #444;
}
${base} p {
  font-size: 12px;
}

component.js

import {base} from './component-style.mcss!';

// etc for whatever framework we're in
export function render(opts) {
  return `
    <div class="${base}">
      <p>stuff</p>
    </div>
  `;
}

That is, the className of base is a dynamic name that is shared as an export of the CSS module, which itself then generates the CSS class names for us.

Say in development, the className could be generated as component-style-base-0, with uniqueness managed, and then in production it could be reduced to z2d on build / bundle.

In this way we get around the biggest modularity hurdle in CSS - the global namespacing of classes.

The syntax may need revising, but it would be great to see something like this if anyone is keen on working on it please let me know!

The other cool thing is it could be done in a way that is backwards compatible with the CSS plugin here, in which case we could ship it in this default CSS plugin with jspm.

@guybedford

This comment has been minimized.

Member

guybedford commented Apr 23, 2015

@geelen while we're on the topic of CSS, I'd be really interested to hear what you think of this.

@geelen

This comment has been minimized.

geelen commented Apr 25, 2015

Very interesting! This doesn't seem particularly difficult to be honest. I really like the syntax it gives you for including the CSS into the file. Personally, I'd borrow something from web components, the :host selector in place of the ${base} one, but that would only let you export one class. That works for me, though, since I usually only define a single class then use the direct descendent selector > to target each tag within the object.

So, if this worked like my postcss plugin, then each file could simply map :host to .component-style-base-X and keep track of all the classes currently being used. Then instead of returning a noop from the fetch, it would just return a JS snippet that exports all the variables. Actually, this would be pretty damn simple to build on top of plugin-postcss, but because the export behaviour is different, it wouldn't necessarily be something we could add to plugin-postcss.

But the <link> injecting and live-reloading from plugin-postcss could definitely be shared. I like this a lot :)

@guybedford

This comment has been minimized.

Member

guybedford commented Apr 25, 2015

Thanks so much for the feedback. The idea was pretty much entirely from https://medium.com/@jviereck/modularise-css-the-react-way-1e817b317b04.

One useful thing of allowing many names is that you don't run the risk of a selector like:

:host p .class {} 

Spilling over a child boundary and into a child component, as all the class names that might cause issues get to be unique. > is perfect for this, but users will make mistakes with it.

So I do think many names is a useful feature. Suggestions for better syntax though are very much appreciated. :${customName} perhaps?

Yes - it's very simple and can just be an output of the instantiate hook. If we could converge on a single CSS plugin, I'd love to try and have something like this in there.

@guybedford

This comment has been minimized.

Member

guybedford commented Apr 25, 2015

We should even try and think of something that we could forseeably imagine becoming a CSS syntax in future. No reason not to shoot for the long shot.

@geelen

This comment has been minimized.

geelen commented Apr 25, 2015

Css has a custom selector spec, I'll dig out a link when I get home but
it's part of the nextcss pack of processors. Let's use that syntax.
On Sat, 25 Apr 2015 at 2:44 pm Guy Bedford notifications@github.com wrote:

We should even try and think of something that we could forseeably imagine
becoming a CSS syntax in future. No reason not to shoot for the long shot.


Reply to this email directly or view it on GitHub
#30 (comment).

@guybedford

This comment has been minimized.

Member

guybedford commented May 3, 2015

@guybedford

This comment has been minimized.

Member

guybedford commented May 3, 2015

Independently coming up with the same idea is always a good sign... I'd be more than happy to converge on Webpack's syntax here.

@danieldunderfelt

This comment has been minimized.

danieldunderfelt commented May 23, 2015

While both this and Webpacks efforts for local CSS are commendable, I just can't get over the special CSS syntax. Why do you need a new way to declare styles? I don't think we need to write CSS is a different way than we do now.

Say we have a style declaration like this:

// style.css
.container .element { ... }

And we import it with a module loader:

// MyModule.jsx
import Styles from '../css/style.css!'

Why can't we simply extract a map of the imported classes from the stylesheet and do this:

// MyModule.jsx

render() {
  return <div style={Styles.container}>
    <section style={Styles.element}></section>
  </div>
}

And then the bundled output would be transformed like in current local CSS proposals:

.ghlköfgh98fghjlk .dfg8dfgh89jkldf { ... }

Or are there some headaches here I've overlooked?

@guybedford

This comment has been minimized.

Member

guybedford commented May 24, 2015

This would be like building a module system by declaring local variables in scripts to be exports, even though previously they wrote to the global. The biggest argument against would be a loss of full backwards compat with normal CSS.

@danieldunderfelt

This comment has been minimized.

danieldunderfelt commented May 24, 2015

Hmm, my example was probably crappy... the local CSS efforts are pretty new to me. My main fear is that every library or tool that comes out with a local CSS scheme has it's own way of doing it, like every framework had their own way of creating classes before ES6 came along. Converging on Webpack's syntax is the right thing to do. Hopefully, once local CSS goes big, other libraries will follow and not invent their own.

What I tried to question with my example above was why something like :local(.class) is needed. I assume, as this is something I'd like to see, that each component has its styles and the js, as @geelen's Typeslab project illustrates here: https://github.com/geelen/typeslab/tree/master/src/lib/components, in the same folder.

If that's the way this is going, I see no need for :local() as the act of importing the CSS into a component would encapsulate it and MAKE it local, through whatever means. If you'd include the same CSS in HTML with <link>, the styles would be global.

What I'm trying to say is, I don't want to see style syntax coupled with the tool. Sass gets away with it because Sass is huge. If local CSS tooling follow the same explosive growth as JS frameworks, then we're in for trouble if no consensus is reached.

That said, this conversation shows that CSS plans for System.js is moving in a good direction by following an existing implementation.

@guybedford

This comment has been minimized.

Member

guybedford commented May 24, 2015

@danieldunderfelt it's completely natural in the evolutionary process to have different systems trying to do the same thing. Trying to standardize too soon can actually be detrimental here, as one loses out on the ecosystem benefits of having properly explored the space.

My opinion is that we should let these syntaxes go wild, then the time will come for standards later.

As a developer what this means is that modular CSS will be a unique system you have to buy into. And you will have to rewrite your modular CSS system moving between systems of when standards finally come. jspm itself will aim to advocate a single system, although it is not yet clear what this will look like.

When you look at the various costs of upgrade paths I really don't think that's too bad at all on the scale of things.

That said if there was a killer standards-compatible modular CSS proposal that we all agree on today and are 100% certain provides what we all want, certainly we should go with it, but in the initial discussions we've had it's been incredibly apparent that there is still a lot of differing opinion in the space that needs to be tested.

@danieldunderfelt

This comment has been minimized.

danieldunderfelt commented May 24, 2015

Okay, I can totally see the value in a last-syntax-standing evolutionary fight to the death! Is there a place for discussing this where I can listen in, form my own opinions and contribute?

@guybedford

This comment has been minimized.

Member

guybedford commented May 24, 2015

@geelen's gist at https://gist.github.com/geelen/3255bf7b48abad32c68d has some interesting ideas.

@danieldunderfelt

This comment has been minimized.

danieldunderfelt commented May 24, 2015

Thanks! That looks sweet.

Also, I just noticed Webpack's started to explore local-by-default styles. Here's an example: https://github.com/markdalgleish/css-modules-example

@geelen

This comment has been minimized.

geelen commented May 26, 2015

Yeah @markdalgleish and I spent all weekend at CampJS talking about this. Webpack loader now has a module-mode. I'm converting my postcss loader to do exactly the same thing.

Both Webpack and JSPM are in a special position, where we're trying to figure out how to deal with a new piece of functionality: the ability for CSS to export information to JS. Mark mentioned the idea of needing a CommonJS of CSS — a standard way of introducing module-building components into a language that's been designed to only use 1 file & global variables.

css-loader currently has an import syntax for classes now, but I want something lower-level. My current thoughts are here, though they're a little lacking in context unless you've been talking about this all weekend :) https://gist.github.com/geelen/1667acc69a5e9ea65438

@guybedford

This comment has been minimized.

Member

guybedford commented May 26, 2015

@geelen this is looking great! Awesome to hear you're making progress.

It was really nice to see compatibility with CSS variables, which made me wonder if export and import might work more easily with a special scope declaration. The other thing is that if creating a new entry point into CSS as "module", then we CAN allow the class names to be locally-scoped. So to stir the pot a bit with some random ideas which may or may not make sense:

@import url(‘test-landscape.css’) (orientation:landscape) {
  --variable-name: var(—-export-name);
  .local-class: .export-class;
}
@import url(‘test-portrait.css’) (orientation:portrait) {
  --variable-name: var(—-export-name);
  .local-class: .export-class;
}
.local-class {
  /* local-class only makes sense in a local context - does not reference global CSS at all?? */
}
:export {
  --custom-export-name: blue;
  --local-var: "value";
}

Effectively, the export values in a real spec could be JS symbols and not even string values, so that the idea of there being a mangled name behind it can disappear. Of course this doesn't make sense for templating though.

Anyway, just thought I'd share random thoughts - I'm sure you'll come up with a great system for us to use.

@guybedford

This comment has been minimized.

Member

guybedford commented May 26, 2015

That said, the above sense of local does make me see the value in :local. What's the current status of that?

@geelen

This comment has been minimized.

geelen commented May 26, 2015

Ha! I have something quite similar now here: webpack-contrib/css-loader#60

With the idea of 'module mode', it makes sense for all .classNames to be exports. If you want global classes, don't use module mode. And given that, I'm on board with @markdalgleish's idea to use . instead of : to mean "export this".

Getting these stepped wrapped up in postcss transforms is the next step, and hopefully @sokra will move css-loader to use them as well. I feel like we're actually getting closer to the concept of var x = require('y') and module.exports.z = w 😎

@guybedford

This comment has been minimized.

Member

guybedford commented Jun 22, 2015

Well this has now happened.... geelen/jspm-loader-css#3 (comment). Plan is to transition that project into becoming the primary css loader here as soon as it can fulfill all the requirements of this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment