Chase McCoy

Tuesday, 10/16, 2:39 AM

Stream of consciousness thoughts on styling components

Orphaned CSS. What happens when you delete an element or class? Post-compile auditing? Nah. Can you really trigger every possible state reliably?

CSS only makes sense for apps if one person is writing it responsibly.

Take the decisions away from everyone because it’s shitty to hold everyone accountable for changes they make in a global namespace.

Why should we conditionally add a class that styles the thing rather than just conditionally styling the thing itself?

Styles-in-JS aren’t the thing — they’re the thing that gets us to the thing

When dealing with complexity in software development, a lot of the headache comes from managing state.

In the simplest abstraction, an application is a function that maintains state, and performs some action on or with that state to produce an output.

That state could be a lot of different, hard to predict, things: user input, time/date, external APIs, databases, etc. I will leave the problem of application state to smarter people than me. I want to focus on component state.

How can we simplify state management in our application by simplifying it in each component?

Every component is like an extremely small application. In most cases, we give components some data and they perform actions that produce a visual output. Components may be interactive, and receive new state over time. They may also access external state like a database or API. For my purposes, let’s just say that a component follows this formula:

state + internal logic = output

The internal logic bit is the interesting part to me, and is where the styling of a component is handled. With existing patterns, the component uses the input state to identify itself to our CSS, which then applies styles. This means that the component creates its own, internal state. I want to argue that this is an anti-pattern that we should avoid.

The more state an application has, the harder it is to reason about the logic of that application. The same applies to components.

State is challenging because we have to 1) manage the state, 2) track the state as it flows through its domain, 3) give access to state appropriately, and 4) ensure the integrity of state.

Right now, we don’t style our components. We apply styles to descriptors that we then apply to our components. This changes our original component formula to this:

state + internal logic + styles = output

What if instead of separating our state into two layers to produce our output, we just styled the thing itself. Then we would have a 1:1 mapping of state to styles. It would improve every aspect of state maintenance:

  1. State would no longer have to be managed in two places to produce a styled output
  2. State would not need to be tracked between our components and their stylesheets (which is an inherently manual process)
  3. Every component would only have access to its own state, and would not be able to affect the state of any other component (CSS is global and mutable)
  4. Because state would not be duplicated, maintaining its integrity would be that much easier

When we choose not to split a component’s state to apply styles, we treat each component like a pure function:

A pure function is a function where the return value is only determined by its input values, without observable side effects.

No side effects. No maintaining explicit relationships between components and their styles by means of selectors. A component’s output would be 100% determined by its input, and could not be affected by global styles or incorrect mapping between state and styles.

No more of this:

if state.type == primary, then className = “Primary”

.Primary {
color: blue;
}

This is bad because what if we get rid of the type state? Who will make sure that the .Primary class gets deleted? Or what if something else defined styles for the .Primary class? Which one wins? Why should we have to keep track of what the .Primary class looks like rather than just looking at the description of our component?

Instead we should write this:

if state.type == primary, then color = blue

Style the thing, not a description of the thing.