Semantic-Org / Semantic-UI-React

The official Semantic-UI-React integration
https://react.semantic-ui.com
MIT License
13.23k stars 4.05k forks source link

RFC: Theming support #1009

Open levithomason opened 7 years ago

levithomason commented 7 years ago

We get questions and requests for this a lot. Some of which can be seen in #802 #775 #366 #851 and #472. This is not an exhaustive list by any means and excludes Gitter conversations and conversations @TechnologyAdvice. I'm opening this RFC to get feedback and consolidate the brain cycles being spent on this.

The goal here is to support the full theming capabilities of Semantic-UI core (SUI) directly in Semantic-UI-React (SUIR). This is extremely powerful as can be seen in this GitHub remake example, be sure to click the little paint bucket icon next to the notifications icon to open the theming sidebar.

Problem Statement

Apps need themes. Theming should be stupid simple and immediately accessible. Currently, it is anything but. Theming requires cloning a separate repo, with a separate build system, building a theme, hosting it somewhere, then adding it to your project. This is not acceptable.

Ideal Scenario

You should be able to choose a theme right in the project that is implementing SUIR. You should also be able to change theme variables and add component style overrides directly in your project.

You should be able to add not only inline styles to individual components but theme-aware-inline-styles. Example, when the background color is set on a Button, it should know how to handle the variations for basic and inverted and also states like :hover and :active.

Possible Solutions

I've been experimenting with a few approaches to making this happen. Just today, I was shown Styletron, a highly performant "universal CSS-in-JS engine." This is almost identical to the approach I wanted to take. I suggest reading the blog post and checking out the related projects linked in the README. Something very similar to this, or Styletron itself, might be the tool we need to bring theme support to SUIR.

I don't think adding theme support is worth it if we were to consider doing so with a traditional CSS solution. These include preprocessor builds (SCSS/LESS/etc), intelligently importing individual component stylesheets, modifying preprocessor variables, and the like. All traditional approaches to CSS come with several issues such as globals, infinitely growing CSS sheets, no static analysis, no dead code elimination, etc. If we do this, I think it is only worth it if we do so with a novel modern approach that solves these issues as well.

How do we get there?

I don't know. Here is a brain dump.

It's a rewrite

Tools like Styletron bring us closer to be able to pull this off. The daunting bit here is that no matter the tool, we're talking about implementing the SUI CSS spec in another language. This is an undertaking comparable to SUIR itself, which re-implemented SUI HTML in React.

This leads to an obvious point that if this work were produced, it certainly should not be dependent on React. It should be a framework agnostic tool that React components can implement easily. It also should not be dependent on a CSS preprocessor or CSS tool. Again, Styletron has this covered with styletron-react.

The good news here is that we would no longer be limited by the sometimes awkward CSS and HTML required by SUI. We could make changes to the styles along with the components as necessary.

It should be in JS

This should be done with plain JS objects and a smart engine that can generate stylesheets. JS gives you portability and static analysis. Portability means you can easily share and use style objects anywhere, client side, server side, React, Elm, etc. You can even request them as JSON from a server. Static analysis means you can generate selectors, perform dead code elimination, optimize stylesheets, and do just about anything else you desire.

This is where I think Styletron really got it right. It appears they are implementing many of these features based on plain objects.

Bye, bye, className buildup

Yep, no component would ever use an HTML className. Instead, they would compose style objects. The style engine would be responsible for applying that style to the component. Ideally, it would generate an optimized stylesheet, inject it into the page, and assign the required classNames to the component. In fact, this is precisely what Styletron does.

If we're not using SUI CSS nor SUI markup, is this even a SUI project anymore?!

Absolutely. SUI is a language for web components, it is not an HTML, CSS, nor JS framework. Qutoing @jlukic from a recent email, he is actually working toward

...a "W3C for UI", an organization which can create simple written specifications for UI components (what methods, options, behaviors) that developers can freely implement "to spec" in their chosen framework or methodology.

The SUI language would be expressed in our component props still just as it is today. We just would not implement any HTML classes. All our current utils for props-to-className buildup would become props-to-composed-style-object utils. I can imagine this tech aligning far better with the Semantic UI language itself.

Possible Implementation & API

Heads up! These are completely fragile and undeveloped ideas.

Composing Style Objects

SUI provides a common language across all components. Consider "variations" such as color, inverted, basic and fluid. Most components implement most variations. Each variation would then have an associated style object. Changing styles from basic to inverted just means merging the inverted style object into the style after the basic variation object. This is also true with theme information.

This sounds identical to CSS until you consider what can be done in JS compared to CSS in terms of libraries, logic, and functional composition. Color manipulation, context-aware CSS transitions, data-driven styles come to mind.

Child Components

Forgive the extremely unruly code here, but in experiments in this codepen, I've shown some rough ideas on how you could generating themes from plain objects. The important bit in that POC is the use of React context to pass cascading theme information to child components.

Example, there is a default Button style and at the bottom of the pen there is a Panel with a Button child. When placed in the Panel, the Button merges a new style it obtained from the Panel. A mechanism like this is needed to handle composing style objects in replacement of traditional cascading styles. This way, when a Button is placed in an inverted Segment, the Button applies its own inverted style "variation".

A mechanism like this would need to be designed and generalized so that it works with all components. It should be based on the SUI language. If so, then most (hopefully all) nested styles could be rendered unnecessary. Instead of a stylesheet including all possible permutations of component hierarchies, parent components would set the theme context for their domain and child components would apply their appropriate style objects. This way, the theming is more or less "flat".

Config and Provider

We would also need some ability to set configuration at the app level. This would contain the theme settings. I believe for this to work we may also need to include a root Provider component that would be in control the app's theme.


I'll leave this open-ended for comments, doubts, and other ideas.

daaain commented 7 years ago

I got here by looking for a way to make SUIR work with a project which uses Webpack and CSS Modules, and being a little bit horrified when I found out that even if I manually set up a selective CSS import list of only components that I use, we're talking about CSS file sizes like 73KB minified just for Button.

I can't really offer a full solution, but it seems that it should be possible to keep CSS as CSS (or LESS, etc) if the project used Webpack and CSS Modules with the SUI components. The huge benefit with this toolchain is that by using CSS Modules and attaching styles in Javascript, Webpack has a dependency tree of what CSS is being used, see for example: https://css-modules.github.io/webpack-demo/

I think this would be a huge improvement over any CSS-in-JS approach like Styletron as it doesn't require reimplementing styling in Javascript. What I don't know if Webpack is already doing a selective CSS build (or if there's a loader to do so), or if you import a CSS file that will all go into the output. But in any case this feels like a solution with much less friction, and CSS Modules in general feels like a good fit for working with components without style clashes.

levithomason commented 7 years ago

we're talking about CSS file sizes like 73KB minified just for Button.

The button.min.css is only 8.81 kB gzipped:

› gzip-size button.min.css 
8.81 kB

if the project used Webpack and CSS Modules with the SUI components

Unfortunately, I think this is a non-starter for implementation at the library level as it would limit and dictate the build system for users. We have users who use Rollup, jspm, UMD, and others I'm sure.

This is certainly possible in your own project if you use Webpack. Just install semantic-ui-css and import only the stylesheets you need. You can also implement minification and gzip compression at the same time.

Appreciate you taking the time to drop some feedback here.

Un3qual commented 7 years ago

I came here from #1526 just to add that I think styled-components should be considered.

EDIT: Fela's atomic thing looks cool too

levithomason commented 7 years ago

Thanks for the heads up @Un3qual. So everyone knows, we are currently reviewing:

We're also taking any insights and cues from https://github.com/airbnb/react-with-styles.

We're considering these factors:

Our end goals will include:

omeid commented 7 years ago

Please also consider just making the less (or any anything that compiles to css) route as well. CSS-in-JS is not free, it comes with a cost, depending on the implementation, that can be deal breaker for some, specially on the phone.

pex commented 7 years ago

@omeid could you explain those costs you are afraid of? Specially for the mobile world I only see advantages in using CSS-in-JS due to smaller footprints and clearer dependencies.

@levithomason I am really looking forward to see this happening. In addition I would recommend having a look at styled-jsx as well. It ships with next.js and probably is one of the simplest libraries mentioned.

omeid commented 7 years ago

@pex There many different implementations, so obviously the exact cost is on a case by case, but generally speaking, css-in-js creates a lot of parsing overhead (both in JavaScript source, and then execution cost) and memory usage (again, both in DOM and JavaScript) compared to plain old css.

See this one for example: https://www.ctheu.com/2015/08/17/react-inline-styles-vs-css-stupid-benchmark/

pruhstal commented 7 years ago

Might make sense to ship this project with 2 separate CSS stylesheets. One for the critical components that includes grid (with breakpoints), positioning, etc. and another one that would strictly be for colors.

Then I can just import that optionally or include my own with the appropriate loaders in my webpack/next.js config.

brianespinosa commented 7 years ago

@pruhstal you could do something similar to that today by including individual component styles on an as-needed basis for your project. Or you could just compile your own css from SUI that does not include the components that you do not currently use. It's super configurable and if you got creative with your build tools, you could do all of what you propose today with SUI. It's just not part of what this project does.

nmchaves commented 7 years ago

@levithomason Would you consider adding glamorous to the list of libraries to review? It builds on top of glamor and exposes a cleaner API that requires less "song and dance."

Metnew commented 7 years ago

@levithomason Would you consider adding emotion to the list of libraries to review? It has a very good performance + good API that is almost identical to styled-components's API.

levithomason commented 7 years ago

As with the RFC/PR, there hasn't been enough community support behind this to pull it off. See my thoughts here https://github.com/Semantic-Org/Semantic-UI-React/pull/1579#issuecomment-330104160.

We're very much still interested in the idea and would love to help anyone along getting this done. However, the help available at the moment is more than likely in the form of reviews and directional comments.

Closing for housekeeping.

kripod commented 7 years ago

While I was browsing the web for a guide about using SUIR themes through a webpack-based workflow, it quickly became clear that there isn’t a “single source of truth” for doing so in a maintainable way. I was worried that I missed something in either the docs of SUIR or SUI, but then, I found this issue.

For my next frontend project, I wanted to choose SUIR for its declarative, “W3C of UI” approach. The only reason which made me decide to go with Material-UI v1 instead is the lack of good support for theming. While there is a great visual example of recreating a GitHub repo’s page in the docs, doing so is non-trivial and cumbersome.

In my opinion, Material-UI v1 got theming right. Using Gatsby plugins, I can server-render all of their components with ease. For CSS-in-JS, I would suggest going along with glamor for its simplicity and maintainability (in addition to this, glamorous may also greatly enhance DX).

As a temporary solution, it would be great to see theme-specific (e.g. Material) CSS to be deployed inside a SUI(R) package.

levithomason commented 7 years ago

While I would probably go with fela, I agree entirely with your points. I wish I had the time or the team to do it :)

FWIW, Material is one of the 23 core themes that ships with SUI LESS.

kripod commented 7 years ago

I know that a Material theme ships with SUI LESS, but there is still no credible package from which I can just import a (bunch of) CSS and be done without polluting my workflow with additional build steps (and devDependencies).

I personally think that this RFC should really deserve more attention.

levithomason commented 7 years ago

Yep, I understand the issue. Are you able to help contribute?

kripod commented 7 years ago

To be honest, I'm not yet familiar with the project's architecture, but if you could collect a list of tasks to be done, I might be able to help in some of them.

brianespinosa commented 7 years ago

I have this issue bookmarked to put together a proof of concept that replicates the theming possibilities in SUI core. I've got a few ideas about how we would be able to do it but I have not yet found the time to take a deep dive yet. I actually went through all of the theming libraries listed above, and a few others, and also thought fela would probably be the best choice.

If anyone DOES take that deep dive before me, please let me know. I can definitely throw time at this in smaller chunks right now. This will be a big project rewriting SUI core theme styles and files, but will be amazing when done.

levithomason commented 7 years ago

@brianespinosa very glad to hear this! I've been meaning to thank you for all the input and help around here. If you're game, I think we need to make you a collaborator. You could then manage issues/PRs at will and create/delete branches on the repo. LMK if this is a direction you'd like to go.

That said, I have a side project on my machine right now using fela that has accomplished this. It is a proof of concept showing CSS-in-JS, top-level them vars, inline component theming, and retains the SUIR props API. There are some challenges as well such as debugging (atomic class names make quite a mess in the DOM).

What has me a bit torn is how exactly to do this incrementally in SUIR. Ideally, we could just port, say, the Divider component as it is the most minimal. However, we will have conflicting CSS between SUI CSS and our inline styles. The next thought is to drop the ui divider class name from the component, and, while that will work well for the base case, SUI CSS does depend quite a bit on child selectors and may end up styling components based on hierarchy anyway.

At the end of the day, I'm fearing this may be a v2 type of shipment where we are forced to completely replace the stylesheets all at once. Given the scope of that, I haven't advertised to anyone that I've been working on it. I don't want to start something I cannot finish. Up until now, I've just been using the proof of concept in a side project of mine.

This will be a big project rewriting SUI core theme styles and files, but will be amazing when done.

Agreed! It would be seriously epic. If done with fela, then the styles are not React dependant. They can generate stylesheets for SUI core as well. Not only this, they also work in React Native. So, with one major port of styles, we could serve 3 implementations.

Your action is quite motivating. I'm wondering if I should put what I have up on GitHub on a separate repo and we can hash chip away at it. It is quite ugly, but proves the idea.

levithomason commented 7 years ago

Folks may want to checkout https://github.com/Semantic-Org/Semantic-UI-React/issues/802#issuecomment-332976317. It looks like there are enough willing hands to collaborate.

Short term, we can focus on a better way to customize and include CSS, generated from LESS. Probably with a UI as shown in the linked comment above. Longterm, we'd like to get to CSS-in-JS.

@1-14x0r has created https://github.com/openmastery/semantic-ui-theme and added me. Suggest those interested and able get access there so we can try to make some progress. We can layout goals, tasks, and start knocking some out as each has time. Feeling pretty good about the need and drive behind this. Really hope there is enough support to see it through! 🙏

levithomason commented 7 years ago

I've kickstarted the convo, see here: https://github.com/openmastery/semantic-ui-theme/issues/1.

p3nGu1nZz commented 7 years ago

Lots of info in this post. I glazed over it, and will break this out into more detail over in the read me of the new repo. Seems like you guys have really thought out a lot of this. I have the time to code it, i just need help with the deign and testing of it.

Ill set up a gitter room for this in a little bit

levithomason commented 7 years ago

I have worked on similar things for a few years. I have several experiments not mentioned that I do on the side. Being the owner here at SUIR and hearing this issue from users from day 1, yes, I've thought and talked a lot about it 😄

I am so glad to see some action, oh boy!

brianespinosa commented 7 years ago

At the end of the day, I'm fearing this may be a v2 type of shipment where we are forced to completely replace the stylesheets all at once. Given the scope of that, I haven't advertised to anyone that I've been working on it. I don't want to start something I cannot finish. Up until now, I've just been using the proof of concept in a side project of mine.

@levithomason I'd still love to see what you've got. I still want to see how we could replicate the whole theming system. If possible at some point if we could even author some tooling to convert the core SUI style definitions into our separate styles, that would be great. Without something like that, 1:1 feature parity is potentially going to be a lot of work to manage after each new SUI core release (on the styles alone).

If you're game, I think we need to make you a collaborator.

And yes, I'm game.

levithomason commented 7 years ago

Agreed entirely. I first looked at a way to parse the LESS into JS styles. Would need some kind of LESS AST for that. If SUI LESS only used variables, simple regex could do it, but, they use a number of functions as well. Still, it may be worth taking a shot at parsing the stylesheets into JS.

I guess, worst case scenario, we just port them by hand. It would then be on us to manage them from there out. This does free us up more actually, we have quite a few CSS based issues that are out of our control. We also have additional components, like Datetime #1240, that we're trying to build from lower level elements but should instead have first class styles.

Perhaps I'll add two more efforts to https://github.com/openmastery/semantic-ui-theme. One, an experiment in converting SUI LESS to CSS-in-JS styles. Two, my current fela implementation. We can then assess the feasibility of these two efforts in light of the theme editor work as well.

levithomason commented 7 years ago

OK, I've stubbed the 3 experiments over at https://github.com/openmastery/semantic-ui-theme (theme editor, css-in-js components, less parser). I'll extract what I have locally for css-in-js and push it there hopefully this weekend.


I've also added you as a Collaborator here at Semantic UI React :) Welcome aboard! The only limitation you have is pushing to master. You can open/close issues and PRs, create/delete branches, and push to PR branches from forks to help Contributors. Feel free to dive in and please feel that you have ownership and stake here. We are very restrictive in who we open access to so when we do open it, we are quite comfortable and excited to gain the new talent.

You can always reach out to me or @layershifter if something urgent needs pushed to master. As far as NPM releases, this is limited to me ATM.

brianespinosa commented 7 years ago

Awesome on both fronts. Looking forward to digging in to check some of this out. I'll have some time to take a look early next week. Curious on the theming, etc. And thanks!

p3nGu1nZz commented 7 years ago

all great info.. i have some other work i need to focus on until early next week, but it seems like we are all on the same page as far as what we are trying to accomplish

By the sounds of it we will need a converter to get the css into js. if not i think we will have to basically re-build the css which would require a refresh on the layout page your adjusting the styles of,, I think long term it would be easier to maintain the css to js translator, and extend the core for new components or changes to SUI.

aurbano commented 6 years ago

@levithomason I use this framework extensively and I wanted to say thanks for the amazing work done here - this is easily the best frontend framework I've ever worked with :smile:

That being said, this RFC highlights the only issue left to tackle with it - so I really hope one of the 3 experiments you have started on the separate repo works out. I'll try to take a look and provide some feedback or dev time getting them to work.

At the moment in a project I'm working on I use Semantic Less so that we can easily customise the final CSS, but that does unfortunately force you to load the whole thing on first load... Resulting in 2.49Mb only for semantic's css...

screen shot 2018-03-08 at 16 03 39

levithomason commented 6 years ago

@aurbano You can explicitly import only the components you use. That will reduce your bundle size.

Note, you don't ship the less files to users, only the CSS. Also, you must be looking at an unminified version of the entire bundle at that.

Using semantic.min.css I find the file size to be 608k minified and 101k gzipped:

$ du -h semantic.min.css
608K

$ gzip-size semantic.min.css
101 kB

Please cherry pick only the stylesheets you need then minify and gzip them. It will help dramatically as you can see. If you only use half of the components, your bundle should only be ~50k gzipped.

levithomason commented 6 years ago

I'm now testing a roll out of CSS in JS at #2710, feedback and testing welcome!

aurbano commented 6 years ago

@levithomason hmm that's strange - we use the less version, directly loaded via webpack so we can add modifications before its compiled. And somehow the size is several Mb. So I'll have a look through it because there must be a lot of duplication happening.

I'll give #2710 a go, this will be quite exciting! With the support for theming this would be wonderful, with the twofold benefit that css would be loaded as its needed, instead of all at once when the page loads :smile:

brianespinosa commented 6 years ago

@aurbano even if using the LESS version, it still should not be part of your packaged bundle. You should actually be getting a compiled CSS file in your bundle after stringing loaders together. I guess the only way you would want to have LESS in the bundle is if you were dynamically compiling styles in browser with it... but it doesn't sound like you have any need to do it that way or you probably would have mentioned it.

aurbano commented 6 years ago

@brianespinosa Thanks for your comment! Yes that's the weird part, I'm using semantic-ui-less-module-loader to simplify loading the styles with our overrides, and then analysing the bundle with the webpack-bundle-analyzer, which shows the image attached on my comment above.

So I'm starting to think that the bundle analyser might be "resolving" (to put it somehow) the css to its less source, and showing that? Anyway, I'll just have to dig a bit deeper here to see what's going on, but hopefully soon we won't need to have the less source anyway :D

levithomason commented 6 years ago

Reopening as we're now working on this in #2710 🎉

sholladay commented 6 years ago

Just came here to say that I love how the SUI docs allow me to play around with themes for each component in realtime. I really, really wish the SUIR docs would do the same, but they don't currently.

Should I open a separate issue for that?

brianespinosa commented 6 years ago

@sholladay until SUIR has internal support for different theme types, that is probably not something that our docs should have. I can see how that would be misleading to people. We already have people confused sometimes about where the styles come from.

Once we have internal theming and styles though, this is absolutely something I can see us having.

levithomason commented 6 years ago

@sholladay Agreed and also agree with @brianespinosa completely here.

I also want to let you all know there will be some dedicated paid resourcing behind our move to CSS in JS. I've started a v2 branch for this work. We're putting together a manifesto for v2 as well. You can expect very thorough thought process and documentation about where we are going, why, and how.

I'm beyond thrilled about this. We'll be keeping all of what we love about SUIR today, fixing what we haven't liked, and bringing some new great ideas. Part of this effort absolutely will include a theme selector, on every component example.

The v2 Manifesto should be posted by Wednesday next week.

hubertguillemain commented 6 years ago

@levithomason can you please clarify (or let me know where the information is) what backward-compatibility to be expected between V1 and V2 ?

Especially, as I understand V1 was CSS-free, but V2 will handle its own CSS: will external CSS files (such as the ones found on http://semantic-ui-forest.com/ be usable with V2 ?

My team is about to implement for our application the capability to allow our customers and partners to create and use their own theme files independently from us (by importing external santadalone SUI CSS files), I would like to know if this is still a valid solution for the future or if we would better hold this until V2

jscheel commented 6 years ago

So, for clarity, the current approach to css-less code will be replaced by some form of css-in-js? I'm actually quite confused here. SUIR is on 0.81.1, so what is this talk of a v2 branch? Will these be developed in parallel?

hugomn commented 6 years ago

@jscheel @levithomason commented somewhere else I don't remember now that we are getting close to v1, just missing some API fixes. V1 will be maintained with bug fixes and with the "external CSS" approach. After finishing v1, the focus will be on v2, that will be a version independent of external styling approaches. As I could check in the RCS and in https://github.com/Semantic-Org/Semantic-UI-React/pull/2710, the idea is that @levithomason will port the Semantic-Ui-LESS project inside Semantic-UI-React using a CSS-in-JS approach, enabling users to have "in-house" customized semantic components.

levithomason commented 6 years ago

@hugomn, confirmed.

I'm collaborating with Microsoft on the next version of the ideal component library. The goal is to allow the consumer to have full control over state, styling, and accessibility. Each of these concerns will be written in a vanilla JS solution that is portable across libraries and extendable.

We've forked Semantic UI React as a starting point. We are in our rough infancy stage but you can see our progress here: https://stardust-ui.github.io/react (Stardust was the original name of Semantic UI React 👍 )

Once these patterns are more clear and stable, we'll consider how we implement them here in Semantic UI React. FWIW, Microsoft is implementing them in Fabric as well. The hope is to create UI specifications, utilities, and low-level components that can enable companies and library authors to create powerful UIs with minimal code that are fully accessible and themeable.

mikestaub commented 5 years ago

Is this new theming api still being worked on?

We want to start theming our library using the method described here, but also want to avoid having to redo a lot of work. https://react.semantic-ui.com/usage/#theme

brianespinosa commented 5 years ago

@mikestaub if you follow the link to Stardust UI in the comment directly above yours, you can see the progress...

stale[bot] commented 5 years ago

There has been no activity in this thread for 180 days. While we care about every issue and we’d love to see this fixed, the core team’s time is limited so we have to focus our attention on the issues that are most pressing. Therefore, we will likely not be able to get to this one.

However, PRs for this issue will of course be accepted and welcome!

If there is no more activity in the next 180 days, this issue will be closed automatically for housekeeping. To prevent this, simply leave a reply here. Thanks!

Kyle-Lowe commented 5 years ago

I see that this conversation has gone stale, but I wanted to bring to light that styled components have been working with SUIR, but we have recently updated from .84.0 to .87.0 to take advantage of some additional features added to the Popup module and have noticed that you can no longer use a styled(SUIR Element) as the Popup trigger without adding an arbitrary wrapper. Apparently the popper.js implementation cannot find the boundaries of the trigger. Has anyone else noticed any changes that have caused existing styled-component implementations to no longer function? Below is a very basic example of what we are seeing. Thanks for any suggestions/fixes.

https://codesandbox.io/s/semantic-ui-example-ci3e1

layershifter commented 5 years ago

Styled components forwarding refs by default, our components are not using this pattern yet. Workaround is there: https://github.com/Semantic-Org/Semantic-UI-React/issues/3786#issuecomment-532701732