Skip to content
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

Support for CSS in JS / modular styling API #248

Open
basarat opened this issue Nov 27, 2016 · 18 comments
Open

Support for CSS in JS / modular styling API #248

basarat opened this issue Nov 27, 2016 · 18 comments

Comments

@basarat
Copy link

basarat commented Nov 27, 2016

Reading through the FAQ : https://github.com/palantir/blueprint/wiki/Frequently-Asked-Questions Specifically Blueprint with a traditional global CSS stylesheet for a few reasons I would like to argue against the reasons presented using typestyle as an example.

Tooling for inline styles is still pretty nascent, while Sass and Stylelint are wonderful tools we love using.

With TypeStyle you get to use TypeScript tooling for CSS just like you use TypeScript tooling for JSX for HTML.

Regular CSS is easier to for consumers override and theme

Note the case, e.g. You should't need to go into a CSS specificity war to customise components : #184. With TypeStyle you just take a className in your props and use classes to compose the final style. This is mentioned here : http://typestyle.io/#/core And demonstrated below (and you can play with it here : http://bit.ly/2fpylJl) :

const messageClass = style({
  color: 'grey',
  fontSize: '24px',
  width: '100%'
});

const Message = (props:{className?:string,text:string}) => {
  return (
    <p className={classes(messageClass,props.className)}>{props.text}</p>
  );
};

<div>
  {/** Without customization */}
  <Message text="Hello"/>
  {/** With customization */}
  <Message text="World" className={style({color:'red'})}/>
</div>

Note: Using className like TypeStyle, gives you all the power of CSS, something that cannot be done with the style attribute.

Hope that helps, this trick will work even without TypeStyle. This is definitely a learning area, and I present more reasons here as well http://typestyle.io/#/why 🌹 ❤️

@basarat
Copy link
Author

basarat commented Nov 27, 2016

Another example where managing just with js would help, you wouldn't need to give this warning as the CSS can be generated on the fly
image

@jkillian
Copy link
Contributor

@basarat always a pleasure to hear your input and see what you're working on!

Let's say we do decide to use typestyle. Doesn't this essentially force all of our downstream consumers to also use typestyle? With meaningful class names removed, users couldn't write regular CSS that gets along well with Blueprint. I'd be a little concerned about forcing this on users

@basarat
Copy link
Author

basarat commented Nov 28, 2016

always a pleasure to hear your input and see what you're working on!

Mutual respect for all your OSS efforts as well 🌹 ❤️ and Thanks!

Doesn't this essentially force all of our downstream consumers to also use typestyle?

Not quite. Few reasons:

  • Multiple TypeStyle versions will work fine (each creates and manages its own style tag). Consumers are free to use whatever they want and don't need to know if the lib internally uses TypeStyle.
  • TypeStyle is small (~1k). Probably smaller than the monolith CSS that needs to be included right now for a single component without additional work. With TypeStyle, consumer would include/require what component they need to and just get the CSS that is required.

With meaningful class names removed, users couldn't write regular CSS that gets along well with Blueprint

They can write regular CSS with their choice of classNames and they get to chose even the framework e.g. aphrodite or CSS modules and pass in the generated or hand-written classNames

More Notes

  • I was just arguing against the core reasons mentioned in the FAQ. If the reason is there is no clear winner I would completely understand and its a reasonable choice ❤️
  • Other CSS in JS options were not focused on TypeScript and that is the key differentiator.
  • Current blueprint forced SASS down to consumer. I feel managing typestyle is simpler (biased opinion of course).
  • Would be happy to have any palantir team as owners if that is a blocker for adoption.

@llorca
Copy link
Contributor

llorca commented Nov 29, 2016

Sass is pretty great and indeed there's no clear winner for CSS in JS yet, but a solution like TypeStyle is very much something we want to research in the near-ish future

@llorca llorca changed the title Customizing styles : Just take classNames Support for CSS in JS Jan 17, 2017
@hungrysquirrel
Copy link

Consider Styled Components please!

@fgiroud
Copy link

fgiroud commented Oct 29, 2017

I saw in the readme that you are considering using tools for css generation, I really hope you will not move to css-modules or worst styled-components.

My arguments/opinions :

  • sass is a standard, approved and used by the community, there is no winner yet in theses tools.
  • theses tools will really improve your developper experience only, when you build the framework, but for us it will be a nightmare to understand how it works.
  • Another tool to learn, heard of the javascript fatigue ? ;-)
  • This make things almost impossible to understand for a designer
  • Blueprint is also an awesome, pure css framework, it will break this.

When I fist saw blueprint I thought, "yet another react framework", but when I saw how you keep things simple, I though "Ahh finally some people think about the developers and designers that will use the tool everyday", "Finally developers that don't think only about the purity of their code, but also about the great things we will build with it".
I seriously believe that blueprint is by far the best tool to build enterprise class web applications

I hope this (very opinionated) post will help you to take the right decision.

Cheers,
Florian

@graysonhicks
Copy link

I really love the blueprint library, but really hoping to see CSS in JS used here at some point so not having to add two stylesheets. Importing a component would bring its styles, and only its styles, in at a time. Is this on the roadmap? Thanks!

@giladgray
Copy link
Contributor

@graysonhicks CSS in JS is not "on the roadmap" because it amounts to a holistic rewrite of each component. it's a future consideration and certainly something we're discussing internally, but i wouldn't hold your breath.

@lookfirst
Copy link

Something to consider is following the way that MUI does things, which has changed a lot over time, but I think they have settled on a pretty decent approach:

  1. https://material-ui.com/customization/themes/
  2. https://material-ui.com/guides/typescript/
  3. https://material-ui.com/guides/interoperability/

@colinmegill
Copy link

Interesting set of libraries in this vein: https://theme-ui.com/

@LoiKos
Copy link

LoiKos commented May 18, 2020

If at some point you are looking to revamp/rewrite blueprintjs components with CSS-in-JS ii suggest using styled-components and styled-system

If you added these it would be much easier to make blueprint "themeable".

theme.ui is great but import things you don't need as library authors from my point of view.

@colinmegill
Copy link

colinmegill commented May 18, 2020 via email

@adidahiya adidahiya changed the title Support for CSS in JS Support for CSS in JS / modular styling API Jul 15, 2020
@ViggoV
Copy link

ViggoV commented Jul 16, 2020

If it is useful to anyone, styled-components support styling of any component: https://styled-components.com/docs/basics#styling-any-component

import { Card } from '@blueprintjs/core';
import styled from 'styled-components';

const StyledCard = styled(Card)`
  background-color: lightsteelblue;
`;

const MyComponent = () => (
  <div>
    {/* default */}
    <Card>Blip blop dippeti doo</Card>
    {/* modified */}
    <StyledCard>Dippe dabbe hippeti hop</StyledCard>
  </div>
);

The problem that remains, though, is that Blueprint components can't be used in isolation since they depend on a global stylesheet. I wonder if it would be possible (relatively easily) to create a stripped down version of the global stylesheet, which can be applied to a div instead of the global dom? This could be applied with styled-components as well:

import styled from 'styled-components';
import { strippedStyles } from '@blueprintjs/core'; // raw text

const BlueprintWrapper = styled.div`
  ${strippedStyles}
`;

const MyModularComponent = () => (
  <BlueprintWrapper>
    {/* blueprint components */}
  </BlueprintWrapper>
);

@giladgray
Copy link
Contributor

giladgray commented Jul 24, 2020

some thoughts on theming Blueprint from an original author of this library (I left the company in early 2019) who has recently attempted this himself in a non-Palantir codebase. apologies for length (my first github essay!) but i have a lot of thoughts here and this is possibly the most contentious feature request in this project, so buckle up.

import { strippedStyles } from '@blueprintjs/core';

@ViggoV this suggestion of strippedStyles is spot on and we should be so lucky to live in such a world! something I think about a lot these days is reconciling the two flavors of CSS: layout and presentation. Blueprint was designed to provide both of these at the same time with such quality that you wouldn't need more, which works really well inside Palantir. what you are asking for is the isolated layout styles here so that a component will render correctly (display: flex, etc) and allow you to bring your own presentation styles. sadly, that is not how Blueprint is built. (but that is how i would build it were I to restart today.)

(an aside: this layout/presentation dichotomy actually also applies to the list of components—Button vs OverflowList—and it's my hypothesis now that "what the people truly want" is a set of high-quality advanced layout components to which they can bring their own visuals. but that's for another library.)

so here are a few things you can do to customize Blueprint today:

change the Sass variables

changing the $pt-* variables themselves is the most reliable way to tweak Blueprint to your needs. I say "tweak" because you're still beholden to the actual CSS of the various components. you can change values but not rules. you can change the hue of $blue3 and the size of $pt-button-height, but you can't (happily) change the background gradient & hover logic of a button.

note that this approach requires compiling blueprint.scss from source and involves one or two hoop-jumps. fortunately, I just packaged up a script i wrote for this as an npm package: https://github.com/giladgray/blueprint-sass-compile. see the repo for full instructions.

it's just CSS

the brute force approach is to simply write your own CSS in your own stylesheet and apply it on top of blueprint.css. this is far and away the cheapest solution up-front but deeply frustrating to maintain in the long term.

the sanest way to do this is to add your own className to components and style that. you can even combine it with the .bp3-* class name for an extra level of specificity: .bp3-button.my-button & <Button className="my-button" />.

the ✨magic of CSS✨allows you to override/clobber any and all styles, although i can't promise it'll be fun. you're going to write some gross styles. your main enemy will be specificity because blueprint involves some highly specific CSS selectors. an appropriate use of !important is to override external styles that you cannot edit yourself.

make your own Button

i suggest creating your own wrappers for common UI components like buttons, inputs, etc. This component does not need to be more complex than a wrapper for BP Button; its purpose is to provide a single place for your customizations.

this pattern has another advantage: it isolates your codebase from UI kit implementation changes. if your entire app uses your own MyButton then you could (in theory) swap the underlying Button implementation without updating all your imports or usages!

here's a basic skeleton:

import { Button, IButtonProps } from '@blueprintjs/core';
import classNames from 'classnames';
import React from 'react';

export interface MyButtonProps extends IButtonProps {
  // add custom props here
  bold?: boolean;
}

// you can also totally use something like styled-components in here!
export const MyButton: React.FC<ButtonProps> = ({ bold, className, ...props }) => {
  const classes = classNames('my-button', bold && 'my-button-bold', className);
  return <Button {...props} className={classes} />;
};
MyButton.displayName = 'MyButton';

@LoiKos
Copy link

LoiKos commented Jul 24, 2020

I can't speak for other people but in my case the problem is not really related with Blueprint Component behaviour. It's pretty easy in fact to override Blueprint component even using styled-components. Problems come when you try to build a new component for your project and you want to make him look alike blueprints one in terms of color or margin.

The real problem is that there only a few documentation and for example you can't know that a button minimal background hover is rgba($gray4, 0.3) unless you look into sass code directly. For me styled-system theme would be enough but i'm not sure it's for everyone.

@ViggoV
Copy link

ViggoV commented Jul 24, 2020

@giladgray Thank you for the detailed answer. If I understand you correctly it seems that you misunderstood me. In the strippedStyles examples I am actually asking for both presentation and layout styles, but packed in such a way that it can be applied to a sub-element rather than globally. In terms of overriding presentation styles I think the different approaches are somewhat satisfying (override sass-vars, apply new class, wrap in styled-component etc.) and could probably be improved just by adding an overview of the css-properties of each component to the documentation.

The exact reason I was looking into this is that we are slowly working towards migrating our massive applications frontend to a react (and presumably blueprint) based solution. However, I am the sole frontender at this point so I replace different parts of the application with standalone modules when they need to be updated. Currently I can't use blueprint for that since it would force me to inject global styles that would mess with the current layout/presentation.

I am sorry if this goes a little off the path for issue, but I was directed here after adding a new issue on the topic.

@ViggoV
Copy link

ViggoV commented Nov 25, 2020

I am having some partial luck with a dirty, hackish solution. I import the raw contents of the global stylesheets and regex out the html and body wrappers. I then use styled-components to create a "style provider" wrapper, which unsets all other styles and include the contents of the Blueprint stylesheets under its generated class name:

import styled from 'styled-components';
import bp_css from '@blueprintjs/core/lib/css/blueprint.css';

const unwrap_rex = new RegExp('^(html|body)\s?{([^}]*)}', 'gms');
const unwrapped_bp_css = bp_css.replace(unwrap_rex, '$2');
// doing the same with the icons css and normalize

const StyleWrapper = styled.div`
  *, *:before, *:after {
    all: unset;
  }

  ${unwrapped_bp_css};
`;

const MyIsolatedApp = () => (
  <>
    <h1>This is styled by the global styles</h1>
    <StyledWrapper>
        ...everything in here has Blueprint styles
    </StyledWrapper>
  </>
);

The above does apply Blueprint styles within the wrapper as expected, but something is wrong with the box-model. Even though everything I've put in there has had display: block, float: none and box-sizing: border-box applied according to Chrome devtools, everything is mashing together as shown below. The brown box contains the StyleWrapper with an
<H1> and a <Card>.

Screen Shot 2020-11-25 at 16 31 35

PS: sorry for the nasty "this is a test - do not use" styling

@nerdstep
Copy link
Contributor

A lot has change in the React ecosystem since this issue was opened back in 2016. Specifically, React Server Components (RSC) are now a thing, which are being implemented in popular frameworks like Next.js. However, RSCs do not work with CSS-in-JS solutions (at least none that I'm currently aware of). As such, many UI component libraries are starting to move away from this approach in favor of purse CSS approaches that can be supported by server rendering. I think it would be prudent to consider this when evaluating a new approach for Blueprint.

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

No branches or pull requests