CSS Structure for Application Styles

Hey all,

I wanted to start a thread to discuss adopting a CSS structure within Foreman and Katello. As we’ve begun working on React pages in Katello most of our styling and structure comes from patternfly via patternfly-react. However, there are some cases where we need some css to glue together the different components. This style is typically applied in one place and has a small footprint. I’m hoping we can standardize on an approach for these styles.

I propose we adopt a CSS-in-JS approach and use a popular library like styled-components or glamorous. If you’re not familiar with this approach you can learn more here. This would allow us to tie styles directly to components and then reuse the components where needed. There’s no challenge of writing classnames or worrying about the global namespace (classnames are still used but they’re generated by the library and specific to the component). This also standardizes the way styles are written in a very clear way. We’ll have the full power of css along with lots of other benefits like passing variables from js to css, direct relationship between styles and components, and much more.

There is certainly no shortage of options for CSS structure including good old Sass, css-modules, etc. Feel free to share your ideas, thoughts, and recommendations. Thanks!

Here are a couple more resources on the CSS-in-JS topic:

Hi,

I personally don’t like this coding style, when you mix styles and javascript, it just feel weird.
well, i said the same staff about jsx and now i’m a big fan :wink:

Anyway, i do think css-moduls are really cool and can fit well in our ecosystem.
It will even make more sense when we will migrate to the file structure suggested by @amirfefer here:

so you will have

product/product.js
product/product.test.js
product/product.scss
product/productActions.js
product/productActions.test.js
product/productReducers.js
product/productReducers.test.js

I think the css-in-js approach would fit our use case since we are getting most of the styling from patternfly-react, it would be a way to avoid global namespace and add small tweaks to components as needed. I don’t have any experience using that method of styling so this is just what I think from reading up on your links (take that for what its worth).

I have no experience with any of the two approaches. From what I understood they both help with avoiding global namespace. Just from what I was able to read about it I tend to like the css modules way more (I also like Amir’s suggestion about the folder structure).

How would each of the approaches solve sharing variables (like color definitions) from core to plugins and from current Foreman’s scss to component styles?

Hello,

In my opinion, a large part of the push and hype around these new approaches stems from JS developers who don’t want to bother learning how to work with CSS properly or want to do everything in JS even when it might not make sense.

CSS provides easy, consistent ways to provide elements with styling that is easy to understand and override.

In most cases, I don’t see any benefit to in-lining the CSS (whether in dynamically generated style tag or in style attribute) over plain old (S)CSS.
I do see downsides to it:

  • Styling is (much) harder to override from outside the component.
  • It potentially increases download file sizes with styles embedded multiple times in JS, and prevents caching of styles shared across multiple pages and components.
  • It adds unnecessary complexity to the JS codebase, handling styles that aren’t related to the component logic.
  • It adds overhead to JS processing as the browser has to calculate the style in runtime from JS code.
  • It may trigger multiple page redraws in the browser, each time a new style tag is encountered (which can be dozens per page in some of these approaches).

The only case where it may make sense to use these approaches is when there is complex logic that affects the desired display, such as for progress bars, and even in those cases I’m not sure this is worth the effort and can usually be achieved using simple css and js.

Overall, big :-1: from me to this approach. I fail to see how this solves any actual issues in our current state, and only wastes effort rewriting working code (potentially breaking styling in places we didn’t anticipate, such as plugins) instead of focusing on giving better value to our users.
The only thing I do agree about, is that styles specific to a single component should live in the same directory as the component, as a .scss file that is compiled into the same stylesheet that serves the entire application. This will allow us to easily find styling for a component rather then go over larger general scss files and prevent dead code related to components which we no longer use.

That’s a great question - I asked @sharvit about it who’s been working on the build process and he mentioned it’s possible in both approaches. The css-in-js would need the variables to be available in the javascript files.

This comment doesn’t help make a decision and it makes it difficult to have an open discussion. There isn’t a proper way to do CSS, there are just different ways to do it. The approach should be judged on it’s merits and not based on the community that’s developing it. If an approach makes CSS more approachable for JS developers that’s actually a benefit, not a drawback.

With that said it doesn’t seem like there is much awareness or experience with CSS-in-JS in this thread which may be a valid reason for not adopting. Personally I’m fine with whatever approach we take but I’ve used the libraries I mentioned and I love the component feel they bring. Since our application CSS is so light it seemed like a fair place to start.

As I mentioned I’m working on the Katello build which doesn’t have any application CSS yet so we’re starting fresh. I’m certainly not recommending rewriting components.

A couple thoughts on the downsides you mentioned:

  • Styling is not harder to override. These libraries add generated classnames to your components. I think CSS-in-JS is often conflated with inline styles.
  • The file size increase would simply be the inverse of the css file that would otherwise hold the styles - net zero
  • The styling added here would always be tied to a component so it’s not adding complex logic to the JS
  • The JS processing of styles is negligible. Hardly more than the alternative of dynamically updating classnames. Plus it’s much more declarative.
  • I’ve never heard of dozens of redraws or style tags… maybe that used to be an issue?

If you haven’t tried the CSS-in-JS approach I think it’s worth trying it out. We’re in a world where things are changing all the time and what used to seem crazy, like JSX, is now the norm.

I’m sorry if my phrasing was unclear, I was not referring to anyone on our team, but rather to a feeling I get regarding the general trends in front end development in recent years.

My main disagreement is regarding these merits and their relevance to our project, not regarding the community that developed them.

I disagree. The issue here is not making CSS more approachable. I think all front-end developers should have a good understanding of how CSS works well so that they can write good, efficient CSS. It has nothing to do with whether that code is placed in a SCSS file, inline style or CSS-in-JS. I am concerned that the latter two may lead to inefficient code and bad practices.

Lack of knowledge among our team is indeed a reason not to go down a certain path, but isn’t a show-stopper necessarily in my opinion - we have very talented people who can learn new things well.
I do feel though that these approaches are very new (all articles are from the last year or so) and have not been battle-tested enough nor have they stabilized yet on new, widely agreed upon best practices. Just for CSS-in-JS there are already tens of different libraries, which one do we pick? how do we know it won’t be abandoned next year and we need to migrate?
Considering the time scales that we support our codebase, I would be weary of adopting cutting-edge technologies or practices unless they bring us clear, significant value (as was the case with react for example).

That is not exactly precise - Katello does inherit many styles from the foreman codebase which in turn relies on patternfly styling. Katello also includes Bastion which adds a bunch of styles to the compiled assets.
Any new component defining its styling separately may need to replicate code or rely on existing classes.

I was referring to both approaches, CSS-in-JS is easier to override the inline, but only if you know the class name to override. Depending on the specific library chosen, that may be simple or not.

Unless you share styles across multiple components, in which case you duplicate the code for all components. But I agree this isn’t a significant concern for most cases.

So why do we need to handle it in JS at all rather then just define it in CSS? The main benefit of doing it in JS IMHO is that it allows JS logic around it, logic that we don’t actually need.

Is it? Rendering the CSS into the DOM requires the JS to parse the CSS string, possibly calculate some variables in case we use such, create a style element and inject it into the DOM.
The styles are also injected in runtime, so they aren’t cached.
why do we need to dynamically update classnames?

Take a look for example at https://emotion.sh/ - one of the CSS-in-JS libraries. For a very simple page, there are several dozen style tags injected into the <head>. Depending on when each style is added by the library, this will potentially cause a paint event per style tag. Especially when we are moving towards a more single-page-like experience, this can cause increased perceived slowness when navigating between pages that change the included styles dynamically in runtime.

TBH, I feel the rate of change in the JS world right now is too fast and won’t be sustainable long term. I could spend all of my work hours reading and trying new libraries and still never catch up with the latest developments. I’m willing to try new, rapidly moving technologies when I see that they bring a significant benefit with them. Right now I don’t see the benefits this brings us or what problem this solves for us.

2 Likes

Wow, awesome points and I totally agree with what you’re saying. Thanks for taking the time to explain your perspective in more detail. I think for now it makes sense to continue using the scss files we have been for the minimal application styling that’s needed.

1 Like

I do like the fact we can make the css transparent to usage with components.
Before using components, we wrote css and the classes act as kind of an API.

For example,
I will create a container class and whenever we want to apply a container behavior in our html,
we will use the container class.

When using components we can deliver a better API.
We can do stuff like:

Container.scss

.container { width: 300px; }

Container.js

export const Container = ({ children, ...props }) => <div className="container">{children}</div>;

So now I have a better API around the container, instead of using the container class, I can use the container component.

import { Container } from './components';

<Container>
  <p>some text</p>
</Container>

Be honest! how many times you found a class in your html and search this class over your whole project?

Together with css modules it will be even easier.
Can you really create a class called container? won’t it interfere with bootstrap container?
So you can do:

Container.scss

.container { width: 300px; }

Container.js

import styles from './Container.scss';

export const Container = ({ children, ...props }) => <div className={styles.container}>{children}</div>;

It will compile to

.container-[some-hash] { width: 300px; }

And the styles.container will contain the container-[some-hash].

1 Like

I definitely like being able to link components more directly to the styling. Great examples!

What are the changes that would need to happen to integrate CSS Modules? I’m assuming there’s a build step that makes the .scss file export what’s needed inside the js?

I have no particular opinion on this subject but one thing I haven’t seen in this discussion is the interaction with plugins. How stable of an API can we provide to them? Would they need to copy a lot of things? Would it be better, equal or worse compared to the current solution?

After just coming back from devconf I heard customizing Foreman with their own plugins - even if they’re just UI changes and no database changes - was a big plus.

It should be very simple to do, i will create a pr soon :slight_smile:

1 Like

With css-modules it should be really simple, just import it from a js file and webpack will link them together.
You should even be able to use variables and mixins.

In a plugin:

Container.scss

@import 'variables';
@import 'mixins';

.container {
  width: $container-width;
}

Container.js

import styles from './Container.scss';

export const Container = ({ children, ...props }) => <div className={styles.container}>{children}</div>;
1 Like

If I needed to override the styles in this container from a plugin how would I know what [some-hash] is in order to override it?

Great question.

  1. Override variables

    // my-plugin/webpack/index.js
    import '../variables.scss';
    
    # my-plugin/webpack/variables.scss
    $container-width: 400px;
    
  2. Use tags

    Container {
      background-color: #FFFFFF;
    }
    
  3. While consuming

    // my-plugin/webpack/PluginLayout.js
    
    import { Container } from 'foreman';
    import styles from './PluginLayout.scss';
    
    const PluginLayout = ({ children }) => <Container className={styles.container}>{children}</Container>;
    

I think you are missing the point Walden was asking about - how is it possible to override these classes outside webpack/react code? With regular CSS it’s easy - just create css with same or higher specificity. When you don’t know the class name and it’s random, it isn’t possible any more.

1 Like

@tbrisker - Like you said, it is impossible overriding the class itself.
It is still possible to override styles, not by overriding the class itself.

As we discussed before, it should get more and more rare that we actually writing styles.

  • The styles should come from patternfly (as none css-module) and it should still be easy to override.
  • When we will write styles/css, it should usually be a temporary fix or a layout fix.

Without taking sides directly, I’ll remind everyone of the old sysadmin saying that temporary fixes have halflives measured in years… :wink:

1 Like