Closed japgolly closed 9 years ago
@chandu0101 said:
Recompile per CSS change = slow dev.
There is a trade off here , prod issues isolation is trivial, faster fixes! 1 sec prod >>>> 1 sec dev !
FYI I really don't think that inline styles (in the literal sense) are the right solution. I think that style application should result in classnames/ids being applied to tags that refer to some generated css.
No matter solution, the actual usage in Scala should be very simple and will probably look the same as inline styling.
Goals:
Haoyi brought up cascading. It's a serious problem. I said: "I just tested inline vs decl styles and cascading happens in both. That situation will require thought. If it's not solved then the "have no surprises" goal will be mostly lost."
<div style="color:red">out<div>in</div></div>
<div class="red" >out<div>in</div></div>
thanks mate for bringing important concerns to discuss
here are my thoughts (from a inline styles fanatic point of view)
1) more performant for browser rendering and React diffing.
I agree we definitely need serious benchmarks from real world apps. here are some benchmarks for css vs inline From @pspeter3
Mainly these two JsPerfs, http://jsperf.com/class-vs-inline-styles/2 and http://jsperf.com/classes-vs-inline-styles. However http://jsperf.com/inline-style-vs-css-class/9 does seem to imply the opposite. Bad for performance was an overstatement. I'm sure you can also cause bad performance with stylesheets by using poor selectors.
.
2)Pseudo-selectors work.
On solution can be we can create stylable tags!
object StylableAnchorTag {
trait Style {
def tagStyle: TagMod = Seq(textDecoration := "none" ,cursor := "pointer")
def onHover: TagMod = Seq[TagMod]()
}
case class State(hover: Boolean = false)
class Backend(t: BackendScope[Props, State]) {
def onMouseEnter(e : ReactEventI) = {
t.modState(_.copy(hover = true))
if(t.props.onMouseEnter != null) t.props.onMouseEnter(e)
}
def onMouseLeave(e : ReactEventI) = {
t.modState(_.copy(hover = false))
if(t.props.onMouseLeave != null) t.props.onMouseLeave(e)
}
}
val component = ReactComponentB[Props]("StylableAnchorTag")
.initialState(State())
.backend(new Backend(_))
.render((P, S, B) => {
val styles = styleSet1(P.style.tagStyle,P.style.onHover -> S.hover)
a(styles,
onMouseEnter ==> B.onMouseEnter,
onMouseLeave ==> B.onMouseLeave,
P.other
)
})
.build
case class Props(onMouseLeave: REventIAny, onMouseEnter: REventIAny, style : Style, other: TagMod *)
def apply(onMouseLeave: REventIAny = null, onMouseEnter: REventIAny = null ,style : Style = new Style {})(other: TagMod*)
= component(Props(onMouseEnter,onMouseLeave,style,other))
}
//usage
StylableAnchorTag(style = new Style {override def onHover = Seq(color := "red")})("Hola"),
3) On-the-fly page-wide style changing possible directly in browser
This boils down to slow dev issue!, again 1 prod sec >>>> 1 dev sec!
4)Can work with external entities such as HTML, designers, etc.
I mostly worked as backend dev so can't talk about this in practical perspective. But we are in a rapid changing ( for better!) industry , developers working hard to cope up with current trend , all other professionals(designers , ... ) must understand this and should give support from their side too , Product is a teamwork afterall ! .
@vjeux could you please share your thoughts on these issues..(especially for 1 and 4 from a practical perspective)
1) I've never seen style resolution yet being the bottleneck. Lots of people are worried about perf but I've yet to hear anyone said it is bad in practice.
4) We're heard this complaint about React since we announced it. In practice, designers are not stupid :) JSX and styles as JS objects look extremely similar to html and CSS so they are able to contribute without much problem. I've also heard succesful stories with designers helping on clojurescript codebases.
I like the idea. I wonder what it would be like to have a css preprocessor like less or sass but with the full power and type safety of scala. It would definitely be awesome to use things like go to definition and refactoring across html, css and js without resorting to grepping for strings.
As for using pseudo-selectors for inline css, the ones i encountered so far using react were easy to implement by either keeping track of some state (like the :hover
example from @chandu0101) or simply implementing their logic like for :first-child
.
Some rough notes/ideas so I don't forget:
padding
), and attributes that affect its surroundings (like margin
). This could have interesting implications for what constitutes conflict, maybe a separation between style & layout....final
would allow us to prevent overriding and catch it at compile-time). It would be messy but it would reflect reality ie. the browser's final in-memory CSS model. At least in an IDE you could jump-to-def to see where unexpected styles are coming from.Reading this thread got me really excited, you guys are reaching for the holy grail of web dev. A single language, full stack, with type safety and compiler checks. I'm sure you have already done this, but reaching out to the scalaJS / react communities directly could yield some interesting feedback, especially on the inline css vs classes debate.
Haoyi said:
What is it that people are asking for that is not provided by less, except for type-safety? Would a type-safe less that could be embedded directly into scala be sufficient?
- Programmatic generation of styles from Scala/Javascript code
- Jump-To-Def, Rename, and other refactorings (IntelliJ does it's best, but even it can't do much)
- Hygienic class names, mangled to prevent clashes
- Opaque class names, "impossible" to construct using string-stitching
- Minification of class-names together with the generated HTML using them (only possible because of the above)
- Dependency analysis and dead-code elimination of your CSS, removing what is not used by the (Scala.js-generated) HTML
LESS or SCSS (which we use at work) is nice but provides none of these things ^_^
@austinrivas I know! It's an exciting prospect hey! I hope we reach a good solution. Re: your advice, I've reached on the SJS ML here.
I personally wouldn't like to push pseduo-selector logic into call-site/components, especially adding state. Luckily for my preferences, it wouldn't work as a full solution. :link
and :visited
can't be determined by JS (right?) so we'll need something else. /cc @lucidd @chandu0101
@japgolly I've been doing a fair amount of MeteorJS development for the last two years, so I know the benefits of having a unified full stack environment. The difference between what you have in mind and the Meteor ecosystem is that Meteor was trying to introduce javascript everywhere, which compounds the issues inherently present in JS apps.
If we can pull this off it will allow for the same speed of development and code sanity that Meteor offers, while using an enormously more powerful language than javascript.
I tend to agree with you that classes make a lot more sense, especially when you look at the inline hell that some other client js frameworks can encourage ( cough Angular cough ).
@austinrivas That's pretty cool, so are you saying that their solution is great wrt styles/css, but it's let down only by its choice of language? What would you think they got right and worked well for you?
I personally wouldn't like to push pseduo-selector logic into call-site/components, especially adding state. Luckily for my preferences, it wouldn't work as a full solution. :link and :visited can't be determined by JS (right?) so we'll need something else. /cc @lucidd @chandu0101
I never used visited/link pseudo classes , but there should be a way to do in js (may be a little more work - Simple means not easy - Rich Hickey)
I tend to agree with you that classes make a lot more sense, especially when you look at the inline hell that some other client js frameworks can encourage ( cough Angular cough ).
I think "inline hell" phrase is borrowed from html inline style days , in react inline styles are a bit different we do have stylenames/classnames in code ,but in runtime we only see style attr's for a particular tag/element ( i would argue that this is a lot better(correct way!) than having many class names with overridden styles)
keep up the flow guys , It doesn't matter who wins(inline/css) ,we'll have a better system at the end of the day!
I'd like to add "responsiveness" as one design goal. Currently it's being implemented as clunky @media rules, which have quite a lot of limitations. For touch controlled devices you'd want to have more changes than just basic grid layout, for example larger buttons etc. With programmatic styles the application would have much more control.
@chandu0101 @japgolly seems like it was possible at one point (at least jquery could do is) but has since been disabled in some browsers because of security implications (source). So getting those to work with inline styles would probably impossible if the security stuff does its job correctly.
Long discussion, and I skimmed so not sure if this has been mentioned but any chance of type safety of values? e.g., say color accepts only Color.red, or Color.fromRGBA, or "auto", or "inherit". Also, if we're going the lihaoyi/scalatags way, I'd like the ability to add custom css attributes easily.
For color, in React Native I thought about enforcing types there but I couldn't find a good reason to do that. Colors being misused as strings are not a common source of error so it didn't make sense to impose a different way of writing it just for the sake of it. This analysis may be different in the context of scalajs though
@vjeux: You're right, its not something that fails often and I was only giving an example, my actual point being some sort of safety for valid values, e.g. sometimes in hurry, in place of doing clear:both, I do float:both. Also, I'd wager that most of us are somewhat experienced users and are the CSS/HTML tail. Our experiences might not be representative. Also, (I've never used an IDE for HTML, so can't say for sure), but if an HTML IDE provided you completion for CSS values, then for a Scala expert but HTML beginner, writing regular HTML/CSS using IDE would be a better choice (a choice he'll soon regret though :) ). I understand your statement, and obviously we need to prioritize things because there can't be a magical solution that fits every need, but I just want to put across my view
writing regular HTML/CSS using IDE would be a better choice (a choice he'll soon regret though :) )
I don't think you've written HTML in Scala for an extended period of time. Anybody who has will tell you it's completely amazing =P
The safety and tooling you get for it far outstrips anything you have writing CSS directly, and the flexibility is far beyond any templating system you've ever used before that (even React.js's). I don't see why writing CSS in Scala would be any worse.
@lihaoyi I think I messed up what I was trying to say. I was comparing against the imaginary future in which you have this tool in a production state without completion for values as a conscious design decision. I was going to say that the tool's current goals themselves make it better than the scala ide with html completion. I am an old guy who uses vim, but I can completely understand the power an IDE can give so no arguments with you there. Btw, Scalatags is really cool, wish it had some compile time method for typechecking for attrs. (
Happened upon similar discussion on twitter: https://twitter.com/DavidKPiano/status/569546791269609472
Cécile Muller @ wildpeaks 4h4 hours ago @ Vjeux @ DavidKPiano one downside is mediaqueries don't work in inline styles, so renderToStaticMarkup would have to generate a stylesheet
David Khourshid @ DavidKPiano 4h4 hours ago @ wildpeaks @ Vjeux Also: pseudoelements, pseudoselectors, combinators and relationships, destroying the "cascade", etc.
. @ yoshuawuyts 4h4 hours ago @ davidkpiano @ mbrochh @ reactjsnews @ sasscss To me this is the only thing that feels like a CSS replacement — http://gridstylesheets.org/
Colin Megill @ colinmegill 2h2 hours ago @ DavidKPiano @ Vjeux We're releasing something in about 10 days that will blow your freaking socks off. Stay tuned.
I happened to be thinking about this today and here's a very rough idea of what I had in mind- this is based on the requirement of "where else in JS and CSS is this used".
//Shared dir: User.scala
package shared
import scalatags.Text.all._
object User{
val userProfile = "user-profile"
val userBio = "user-bio"
val userName = "user-name"
val userScore = "user-score"
def display(
name: String,
age: Int,
score: Int
) = {
div( cls := userProfile )(
div( cls := userBio )(
span( cls := userName )( name ),
span( s" ($age) " )
)
span( cls := userScore )( score )
)
}
}
//CSS Framework
case class CssContext( selector: String ){
def pushChild( c: CssContext ) // css selector of the form ".class1 .class2"
def pushImmediateChild( c: CssContext ) // css selector of the form ".class1>.class2"
def pushSibling( c: CssContext ) // css selector of the form ".class1+.class2"
def style( s: StyleElements* )
}
//CSS Project
val browsers = Seq( "moz", "ms", "webkit" )
val borderRadiusCustomProperties = browsers mapValues( b => CssCustomProperty[ ( SizeElem, SizeElem ) ]( s"$b-border-radius" ))
val reusableStyleElementGroup = StyleGroup.style(
CssProperty.fontSize := 9 rem, //space between 9 and rem intentional, using post fix operators
CssProperty.textTransform := CssValues.TextTransform.Underline //basically some
//classification is needed so that text-transform can not be set to CssValues.FontWeight.Bold.
//This creates a problem with experimental features though.
//If chrome adds text-transform: disappear, then how do you write that?
)
CssClassContext( shared.User.userProfile ).
pushChild( CssClassContext( shared.User.userName ) ).
style(
CssProperty.fontFamily := Seq( "Calibri", "serif" ),
CssProperty.fontWeight := CssValues.INHERIT
).
//the definition for this style
//for Seqs has not been show above,
//but easy to do using implicit class
style(
reusableStyleElementGroup
).
style(
CssProperty.marginTopRightBottomLeft := ( 1 px, 2 px, 3px, 4px )
)
CssClassContext( shared.User.age ).
pushImmediateChild( shared.User.age ).
style(
reusableStyleElementGroup
).
style(
CssProperty.marginTopHorizontalBottom := ( 1 px, 2 px, 3px )
)
CssClassContext( shared.User.userProfile ).
style(
CssProperty.marginVerticalHorizontal := ( 1 px, 2 px )
)
Here's what it should generate:
.user-profile .user-name{
font-family : Calibri, serif;
font-weight : inherit;
font-size : 9rem;
text-transform : underline;
margin : 1px 2px 3px 4px;
}
.user-profile>.user-age{
-moz-border-radius: ( 2em, 2em )
-ms-border-radius: ( 2em, 2em )
-webkit-border-radius: ( 2em, 2em )
margin: 1px 2px 3px;
}
.user-profile{
margin: 1px 2px;
}
Problems: Css Properties are not first class citizens. You write much more than regular CSS. There's also an issue of experimental features if one's trying to keep values typesafe (see comment above). Then there's also the problem of how to treat shorthand properties. (Naming basically I think). Overall not very clean, but I figured I'll share anyway.
Funnily, I completely unintentionally wrote textTransform in place of textDecoration, which I guess justifies the original idea even more
I've only quickly skimmed this, it may be of interest (and relevance). Will read properly later.
https://en.bem.info/articles/bem-for-small-projects/ http://www.smashingmagazine.com/2012/04/16/a-new-front-end-methodology-bem/
Radium: a powerful complementary tool to #reactjs inline style (MQ, states..) http://projects.formidablelabs.com/radium/ #css
I've created a new project for this upcoming solution: ScalaCSS
I have analysed what we've all discussed and produced a first draft of the requirements of everything I intend this solution to do. Please check it out, review it, and let me know if you love it, hate it, what you think. Certain things have been rejected (like CSS attr value validation - sorry @SRGOM maybe later), and certain proposed implementation details have been ruled out (for which the constraints should explain the reasoning).
Please join me there and on its Gitter chat to continue discussion.
Server code, client JS and HTML are all compiler-verified in Scala, leaving CSS is the last part of my webapp mega-project where I feel unsafe that it always works and is maintainable.
What are the problems with typical CSS? See https://speakerdeck.com/vjeux/react-css-in-js
I'd like to solve this problem in a way such that _any_ questions or concerns that arise in one's project regarding CSS, can be assuaged by the compiler. Examples are:
Free-form thoughts thus far: