Show HN: Myth – CSS the way it was imagined
myth.io
myth.io
I think this is a really cool project, and I commend ianstormtaylor for pushing the envelope and advancing the state of the web. Good job!
edit: I understand criticism has its place in Show HN. But for God's sake, I had to scroll _all the way to the bottom_ to find some kind words of encouragement. You folks should really read some Dale Carnegie
I can't help but think this tendency of ours can't be hacked into something more productive, but I have no solution as of yet. :)
When presented with a sensible, practical and actually innovative idea, I've found the responses to be fairly favorable. With small things that have been done over and over again (worse or better), I think you can expect some comparisons and questions.
For people who didn't grow up [with it].
There is a place for criticism, even non-constructive. But HN sometimes is an echo chamber of only criticism and negativity.
Maybe it's time for a sociological analysis of our tech population.
For example:
<!DOCTYPE html>
<title>CSS Variable Test</title>
<style>
head {
display: block;
var-mycolor: blue;
}
:root {
var-mycolor: red;
}
title {
display: block;
color: var(mycolor);
}
</style>
If I load that document in a browser that supports CSS variables, the title will be blue. But if I run it through Myth, it drops the blue rule and makes the title red. This is because CSS variables are inherited throughout the document and can be overridden at any time. The calculated value of the CSS property that uses the variable depends on the document structure.Likewise with calc() - if you multiply values like in their example, it works, but if you try to add two values of different units (e.g. 2em + 30%), it silently falls back to requiring browser support for calc().
This might be useful in narrow circumstances, but it should have big warning signs because it doesn't come close to being a proper polyfill.
Having a "polyfill" is certainly a valid justification. But this doesn't come close to LESS/Sass -- I'd argue that the main feature of those is nested rules, and then mixins.
Variables and calculations are great, but most LESS code I've encountered uses nesting and mixins to a far greater extent. Advertising the project as "the benefits of tools like LESS and Sass" seems misleading, and seems to set up expectations that Myth doesn't fulfill.
The complexity of Sass/Less is starting to out-weigh the benefit of their features, for me at least.
Having said that, I don't know if this polyfills the var spec exactly.
But even if that is the case, it doesn't make any sense to me, and I can't begin to understand how they could be compatible with each other. How are you going to write "var(...)" or "color(...)" declarations within LESS, that LESS will ignore, just to "pass through" to Myth? And why would you? As far as I can tell, Myth doesn't do anything that LESS doesn't already do, so I don't see why anyone would add Myth on top of LESS.
From everything I've seen nesting rules is an anti-pattern. It's useful for writing code as fast as possible, but for any project of reasonable size maintenance is a far bigger worry than LOC per second, and nesting makes for some horrible to maintain CSS. Until we have native extending in CSS you're much better off going the "one-class-per-element" route for maintenance.
As for mixins, I actually think they are an anti-pattern too. They're usually used for two different things. The first is to simply make vendor-prefixing easier, which this library already eliminates. For example the LESS homepage shows mixins being used when really it's just a regular old `border-radius` property. And the second is for extending classes with groups of properties, but mixins are a bad practice there because they just duplicate the code instead of doing any smart inheritance, so you end up with huge code bloat.
The real missing feature from preprocessors in my opinion is the idea of "extending", but I wanted to wait for the CSS spec for extensions to be more thought out before adding it, in case it varies a lot from how preprocessors do it.
---
The reason this came about is because we went back to writing pure CSS at Segment.io (after having used LESS, Stylus and Sass at different points) and realized that we shouldn't need to give up things like variables just because we don't want the extra weight of a new syntax.
Likewise, I feel like you're missing one of the main points of mixins -- that they allow you to repeat commonly used calculations that may involve multiple properties in complex ways. For example, a mixin I often use lets me create a vertical gradient by specifying a base color and a brightess spread amount, instead of start/end colors. It's incredibly valuable, and it also allows me to automatically support IE and specify a fallback base color. Mixins are hugely valuable for creating classes based on sprite positioning too. Going back to pure CSS without mixins would be a huge step backwards in maintainability for any project of decent size which I've worked on.
Obviously, both nesting and mixins can be abused or used inappropriately, but I'd never call them anti-patterns -- to the contrary, they're essential for front-end development on sites that reach a certain level of complexity, for cleanly factored code.
You're often better off just using BEM style selectors and using the selector name for nesting.
.titlebar {} .titlebar-button {}
As for mixins, yeah, you get some benefit if you're trying to be super tricky. But that button you've created is used once or twice, plus you could just use CSS variables and colour functions.
The question is now whether the added complexity everywhere else is worth that tiny benefit. There are plenty of people who have built large front-end sites and found that Sass/Less in fact make it harder to maintain.
But honestly, this stuff is totally subjective in a lot of cases. Different projects, different tools. If it works for you then stick with it, for other people, Myth and Rework are perfect.
Having written CSS for a decade and decided what semantics CSS had and how it should be structured, I was very surprised when I actually gave BEM/OOCSS/Whatever a go. It works. And I don't like going back now.
This is a contentious subject, so take everything I say as just, like, my opinion. Using generic class names like `titlebar` is likely to result in namespace collision. I define a project by modules, and use a slightly bastardized version of the block, element, modifier naming scheme to prevent this. It's verbose, but I consider the extra typing a reasonable price to pay for the ease of maintenance. For example: `.mod-modal`, `.mod-modal--title`, `.mod-modal--button_large_danger`. A sparse library of grid and spacing utility classes allow flexible formatting without having to write new definitions for every variation of your modules. Content hooks are provided for edge cases where you really do need to override some module styles for a specific page or design element. I use a `myproj-contentdescriptor` naming scheme. I apply these liberally but rarely define them in the stylesheet. The excessively convoluted naming scheme prevents leakage and interference from 3rd party libraries. It makes for some ugly-ass markup, but this is a pattern I've settled on after 10 years of CSS day job.
Edit: Ian just argued the same case more eloquently. This methodology seems to be gaining traction amongst framework authors over the last two years.
---
For nesting, the problem with nesting is that is messed with specificity way too much. Since adding an extra class inside a nested block is trivial, people start adding more and more. But even just 2 classes is already too much in my opinion. Ideally each component would have classes on each of its inner elements, so that the component-level styles always have a specificity of 0010:
<div class="textfield">
<label class="textfield-label"></label>
<input class="textfield-input">
</div>
If you have a setup like that, you never need to nest because your CSS ends up looking like this: .textfield {}
.textfield-label {}
.textfield-input {}
And that way all of your selectors are minimally invasive in terms of specificity. The problem with nesting is that you end up with selectors that have a specificity of 0020, which means anyone implements those components needs to be sure to achieve that level of specificity or greater to override them.But honestly, when working with small components there just isn't that much typing to justify the possible downsides. Sure I have to type out the extra selectors in the beginning a few more times, but its worth it to do without the unintended consequences of the magic.
The one case where nesting really does come in handy is for page-specific or app-specific stylesheet files. Places where you are adding the "glue" CSS that should always be namespaced to avoid collisions.
---
For mixins, I feel like they only seem useful if the components you're using are broken into small enough pieces to begin with. For example, if you want a complex gradient system on your buttons, put that inside your button styles and then don't touch it from anywhere else. If you're constantly needing to use a mixin to re-define those styles, something else is wrong. Because realistically how many different color variations of those buttons do you need to make? It should be somewhat limited, and they should be able to exist in a single file without issue.
Where things really get screwed is if you aren't versioning your mixins (which most people aren't doing). At that point, you're just tightly coupling changes to that mixins across your entire codebase. If you want to change the way the buttons implement the gradient, with the mixin system you go change the mixin, but now you need to visit every other component that uses that mixin and double-check that you didn't break anything.
Instead, if you didn't use a mixin and the component just contained its gradient logic (slightly more verbose maybe, but way more maintainable) then you don't have that problem.
The other solution is to strictly version your mixins using something like Component and maintain your mixins in a separate repository on GitHub that gets versioned, then that also works because each other component can peg the version of the mixin it is using.
---
My big takeaway from CSS though is that none of the solutions for it are nice right now. And most of these problems should disappear (I think) with proper native extension.
But you say:
> If you're constantly needing to use a mixin to re-define those styles, something else is wrong
> realistically how many different color variations of those buttons do you need to make?
> if you didn't use a mixin... (slightly more verbose maybe, but way more maintainable)
That's an awful lot of presumption, and it's just totally wrong, at least in my case. To use my gradient example: I probably apply this model to 50 or 100 types of elements across a site that's not even that big -- to buttons, to backgrounds, to panels, etc. because the designer likes subtle background variations -- flat design, but not too flat. So nothing is wrong, there are tons of variations, and using mixins is the just following the "dont-repeat-yourself" model.
Obviously, you could call this a model of "tight coupling", but that's a good thing, not bad -- you define a single model of mixins for your site, just like you have a single global CSS reset. It just makes up for certain common deficiencies in CSS, and helps in the creation of a common visual identity of your site where CSS classes themselves just aren't flexible enough. (There's another whole reason to use mixins used exclusively within specific components, but you seem to agree on that above.)
The "slightly more verbose" alternative you propose would actually be incredibly verbose (maybe 7 CSS properties per element instead of 1, if used on 100 types of elements, meaning 700 lines of code instead of 100), because nothing's being reused. If a new browser comes out which requires a slightly different tweak, you would need to manually fix it in 100 places instead of 1, which is horrible. Mixins simply allow you not to repeat yourself, which should be the goal with all good programming. CSS without mixins is kind of like programming in Python without being able to define functions.
(And asking people to version their mixins makes as much sense as versioning functions in a C++ project because changing a function changes its behavior. I.e., not much with rare exceptions. If you change a mixin you've been using for a while, then it goes without saying you inspect where it's used in the site to make sure it doesn't break things.)
Not if your UI is of sufficient complexity that you're composing components of other components. Then not only do you have lots of collisions, you have a huge specificity war too. That's why prefixing and single-class-name selectors are generally preferred.
But CSS precedence is always a problem, if it's not specificity then it's ordering instead, the problem doesn't go away by using prefixes. But using the order that rules are defined in can often be more difficult to track/maintain than using specificity to do the same. Good commenting practice is key in both cases. Nesting with mixins certainly isn't always the answer, but it often provides a much cleaner, more maintainable, more intuitive approach to resolving occasional "specificity wars" than not using them.
.textfield {
&-label {
...
}
&-input {
...
}
} .textfield {}
.textfield input {}
.textfield label {}
.textfield {
label {}
input {}
}
etc. <fieldset class="TextField">
<label class="TextField-label"></label>
<input class="TextField-control">
<label class="TextField-legend"></label>
</fieldset>
Just makes it future-proof for when you want to add other elements that might collide since the number of element types is small.Generally I try to never select on non-class selectors in component-level CSS. Component-level styles should occupy the middle range of class selectors. And then the user should have control over the element selectors (base styles) and id selectors (page-specific styles) so that they can both cascade and override when they need to.
// Sass
[data-UI-component="textfield"] {
& > label:only-of-type,
& > label:first-of-type {}
& > input {}
& > label:last-of-type {}
}
/* css output */
[data-UI-component="textfield"] {}
[data-UI-component="textfield"] > label:only-of-type,
[data-UI-component="textfield"] > label:first-of-type {}
[data-UI-component="textfield"] > input {}
[data-UI-component="textfield"] > label:last-of-type {}
That way, you don’t mess-up your template/html with classitis. I like to reserve the use of classes for js logic (states) and for semantics (SEO, schema.org, …), thus using the `data-x` attribute to create and select components.Sure your component’s html/handlebars/whatever template must be kept in sync with its css, and adding elements could totally mess-up things. But I think that’s okay, since changing a component’s template should require a revisit of its stylesheet. What do you think?
Generally trying to tie it to order in the DOM just becomes very fragile, when really a classname is actually more semantic in terms of the meaning sticking with the element and being able to use it anywhere.
I think trying to separate JS hooks from CSS hooks is an anti-pattern actually. Like having `js-` prefixed classes. Because realistically you aren't changing the class names enough for it to be a problem. And if you do change the class names there's a good chance you'd want to change the `js-` prefixed one too and eliminate any benefit they were giving you in the first place.
You might be able to get it to work for simple components though. But I'd be weary since there are only so many HTML different elements, and the special pseudo-selectors can only get you so far.
I'm not a frontend engineer, so forgive my ignorance, but if you could indulge me, what makes "js-" classes bad and "field" and "label" classes OK?
The problem is that if you add "js-" classes that probably means you add the equivalent "js-"-less classes on the element as well. So then you just have duplication with the idea that they are "decoupled". Which might be good if it was true, but realistically if you change the component around to have a different DOM structure you're just going to end up changing both instances of the class anyways.
Really it's just trying to solve a problem that shouldn't be solved. Just agree upon an API (and put more effort into making the API good from the start) and then you don't have the problem to begin with. If you really do need to change the API later (which should happen rarely) then just put in the work to do it. Generally breaking API changes are rare if the component was design properly from the beginning, and if it stays small in scope.
Templates already have too many classes as it is without having to dupe them all :)
http://csswizardry.com/2013/01/mindbemding-getting-your-head...
Basically it's anti-nesting and proposes being very specific with regards to class naming:
.block{} .block__element{} .block--modifier{}
I've experimented a bit with this lately but haven't come to a conclusion yet myself. Yes, it does decrease the chances of accidently styling the wrong elements, but it also makes the html more verbose and can in some cases make it more difficult to implement the website w/jQuery (because of the different classes in use, ie. instead of a .selected class you might have .selected__orange, .selected__apple so to switch the selected item you can't just do an .removeClass('selected')/.addClass('selected)
While it's more difficult to replicate the same issues in native CSS due to the friction involved of copying/pasting the same elements/classes over and over, I don't think that should be a reason to entirely avoid a tool that can be a great benefit in the long run.
On the other hand, per our email I have to say I'm more and more skeptical of the benefit and long-term maintainability of SCSS @extends and @mixin except in special circumstances.
.blah > .blorp
than it is with .blah .blorp
without resorting to .blah-blorp prefixing. .my-class {
font-weight: bold;
// use "placeholder selector":
& > .other-class {
// selector compiles to:
// .my-class > .other-class
border-width: 0;
}
}Sass defaults to .scss syntax, which is exactly CSS syntax. Okay, exactly a superset. Since it merely adds $variables and other features, you are free to ignore them and just write “pure CSS” but with variables.
What am I missing?
This way, I can write SASS to target modern browsers and not worry about backward compatibility so much, because I know the postprocessor is there to catch the older browsers. e.g.: I don't want to worry about specifying a solid block colour for CSS gradients, I just want to specify a gradient.
But yes, I agree the implication that this could replace preprocessors is unrealistic.
Maybe it was that a clear winner never arose between LESS and SASS or maybe that I always let things play out a bit before jumping onto a trendy technology. I guess I'll have to just get with it since it's not really going away.
.header {
background-color: orange;
p {
font-size: 16px;
}
}
You nest the selectors and it spits out .header{} and .header p {}. Much cleaner to write and maintain.(Only relevant if you need to support IE < 9, but still cool.)
A lot of thin fonts look bad in Windows especially chrome. So you could probably make a safe bet that if a font is thin on OSX then it will be much thinner (sometimes unreadable) on Windows.
If you want to use thin fonts only use them for the titles (but even here it looks like that causes problems).
http://i.imgur.com/i5GwqIN.png?1
"Variablcs"?
I you need finer grained control, take a look at rework itself. We have been using it for a while and it's just great.
Taking plain CSS as an input also means you can use Myth to post-process anyone else's CSS, adding the browser support you need, without having to re-write their code in a completely different syntax.
What distinguishes it from LESS or SASS or whatever is that the input has to be valid CSS, as well as the output. Their claim is that it makes your CSS act now like it will in the future once the version of the spec they're targeting has been fully adopted.This project will "enhance" "normal" CSS to take care of polyfill, which alone is a win. Take any CSS file you got, and it suddenly has all browser prefixes thanks to this tool. This is something LESS/SASS can not do.
Postprocessor: css => better css (e.g. myth, ?)
future css => compatible css ?
when I saw "post-processor" I was expecting this to be some kind of browser extension or plugin that post-processes (on the client side) CSS received over the network.
it seems that they have basically just done a pre-processor that accepts css as input and returns css as output. its exactly like every other css pre-processor except that its DSL for pre-processing is a subset of CSS rather than a superset.
But besides all that, it's pretty sweet to have something more like "CSS with variables". That can come in super-handy sometimes.
Also this looks pretty neat; I wasn't super interested in learning to use Sass/LESS or working it into my development cycle, but this looks like a good step towards not having to make much of a change while still reaping some nice benefits.
It's been an open bug on Chromium/Chrome for several years.
It's in Chromium, not sure whether it's in Canary yet.
I totally read that completely differently. Complete document/style separation is a myth.
Seriously great work.
After all, SCSS was based on "CSS3" so we wouldn't have to rewrite our CSS. It's still around ... so we don't have to rewrite our SCSS.
I'm happy to see innovation here, but I also wish IE would just auto-update already. :D
In his world, the best of those features would be part of the CSS standard and supported by browsers, so you could just give them right to browsers without compiling anything.
I know I used to think the var syntax was incredibly ugly, but I have to say after using it for a while now, you actually get used to it.
But read through the CSSWG mailing lists and you'll gain a lot more respect back for the decisions they make.
You say that like everyone agrees on what "the best" is.
Looks a lot like a pre-processor to me, except that its functionality is limited to what can be defined by pure CSS. I'm not sure why you would choose this preprocessor over one with more functionality.
Somebody must have thought it was a good idea to have gone through so much effort, so perhaps I'm missing something.
The font chosen doesn't render properly on Windows with Opera or Chrome.
For anyone wondering, here's what it looks like: http://imgur.com/XbZJpC6.png