rescriptbr / ancestor

:skull: UI primitives for ReScript and React
https://ancestor.netlify.app
152 stars 7 forks source link

Building on top of bs-css vs rolling your own emotion wrapper #95

Closed woeps closed 1 year ago

woeps commented 1 year ago

I saw you invested a lot of effort into writing your own emotion wrapper.

I'm curious if you considered building on top of bs-css? And if so, why you didn't?

vmarcosp commented 1 year ago

I love bs-css and I used a lot in many projects. I considered building Ancestor on top of it but I'm afraid that the project isn't focused on ReScript, since they want to support Reason too. I could change my css implementation to use the "atomic types" from bs-css but I'll keep the API that I created on top of the new optional record api. But, that's a good question, I can use the bs-css atomic types instead of creating mine from scratch (like I did) and create a new api on top of its types. What do you think as a future user?

giraud commented 1 year ago

Hi, bs-css is already entirely converted to Rescript. The goal is to try to stay compatible with reasonml by not using an advanced technique that couldn’t be supported by melange. Also, bs-css/core has been extracted for the purpose of other projects that would like to use the type definitions without the bindings.

vmarcosp commented 1 year ago

Hey @giraud, thanks for clarifying the future of bs-css. Having said that, @woeps I'll build the ancestor's css module on top of bs-css but keeping the same api using optional record fields.

@giraud Just a suggestion, don't you think that the name bs-css doesn't tell much about the project? Why not something like re-css or ml-css, or even rescript-css. bs stands for buckelscript, right? Please don’t take this as criticism 😄

giraud commented 1 year ago

I know, but I always favor backward compatibility and I want to avoid project renaming: it would have a big impact on existing projects and I don’t like that idea. Finally, bs is abstracted enough that it doesnt tie the lib to reasonml or rescript !

vmarcosp commented 1 year ago

It makes sense, thanks for answering 😄

vmarcosp commented 1 year ago

Hey @woeps, I've been studying how to build the CSS package on top of bs-css and I found a limitation: A lot of modules like Border or Padding are dependent on a parametrized type and function provided through module functor as you can see here. The motivation is to create a CSS wrapper that allows you to customize your tokens in a type-safe way. This approach is very similar to the Chakra UI customization tokens (you can see it here) but I'm using module functors to create a type-safe API. Having said that, I'm not sure if I can build the CSS package on top of bs-css due to these limitations. Creating my own CSS atomic types allows me to create type parametrization and customization to provide an API to customize tokens for spacing, border, radius, etc.

woeps commented 1 year ago

Let me try to state the problem you were facing in my own words and please verify if I understood correct:

Problem 1: CSS types parameterized

Since your CSS types are being made aware of custom types it's not a task of just "replacing some type here and there" because bs-css does not work like this.

To solve this, the order of evaluation would need to change:

We would need to verify how much of the code ends up being called during app runtime and which parts could be bundled beforehand to optimize performance and bundlesize.

Problem 2: type identity

Another issue I believe I have found is, that you heavily rely on poly variants not being nomenal typed. Have you tried using other types? I believe the usage of nomenal types (like normal variants) would bring up the issue, that each call to the CSS types functor would create a separate type on it's own. (An issue, since the functor is called multiple times.)

This could probably be solved (looking at this issue exclusively) by calling the css types functor just once and pass this to all component functors. And/Or have module types using with constraints to replace the types in the signature with the custom ones. By doing so it's possible, that all functors use the same nomenal type.

Opportunity to make ancestor css-in-js lib agnostic

If bs-css could be used it might open up the possibility for Ancestor to be used with any css-in-js imementation compatible with bs-css. Edit: I just realized this is already possible by shadowing the default CSS function in the config module. The css function shall take a string (css) and return a class name.
This is just another abstraction, compared to the way bs-css works.


I'd like to find some time to play around with a fork of your codebase and see if I can come up with some feasable suggestions.

vmarcosp commented 1 year ago

Hey @woeps, thanks for taking your time on this. Yes, you understood correctly the problem!

Since your CSS types are being made aware of custom types it's not a task of just "replacing some type here and there" because bs-css does not work like this.

Exactly. Actually, I could use one or two modules from bs-css, but it's not something that worth the effort I guess. There are a lot of modules/types that rely on the parametrized types to create a custom token-based setup.

any user typed value would have to be converted to bs-css core types first

This is an approach, but I prefer to keep the same as simple as possible, just create your own tokens and give the values for each token. This will be possible using bs-css but we can't do this because of the type parametrization.

We would need to verify how much of the code ends up being called during app runtime and which parts could be bundled beforehand to optimize performance and bundle size.

Yep, I'm not sure how to do this yet, because, ReScript libraries are normally a package that provides .res files, and these files will be compiled in the user project and bundled using their own framework/bundler of choice. But we can discuss that part in the future.

Another issue I believe I have found is, that you heavily rely on poly variants not being nomenal typed. Have you tried using other types? I believe the usage of nominal types (like normal variants) would bring up the issue, that each call to the CSS types functor would create a separate type on its own. (An issue, since the functor, is called multiple times.)

Yep, I've tried different approaches. The poly variants are working very well, but I agree that normal variants may deliver a better developer experience. But I think they are working well for now. About calling the module functor multiple times, this is an issue. I've been studying approaches to call the functor once and passing it to all the components/modules that depend on it but I have no idea how to do it without creating a giant module-type definition 😄 Do you have any suggestions?

If bs-css could be used it might open up the possibility for Ancestor to be used with any css-in-js implementation compatible with bs-css.

Yep, we can discuss this possibility. About being agnostic of css-in-js lib, nowadays by default, Ancestor is using @emotion/css. I've changed the implementation that allows css-in-js library customization. But we can discuss the possibility to implement this feature again.

vmarcosp commented 1 year ago

@woeps Since the initial version on top of bs-css was released, I'll close this issue, okay?