Software development dogmas can take a long time to die.
Take Separation of concerns, an important principle of computer science, which, if not understood properly, leads to programs sectioned along inefficient, sometimes arbitrary lines.
This confusion can be seen in a type of recurring comment, an unfunny and unintentional meme, that we see in online discourse. It goes something like that:
- HTML, CSS and JS being distinct languages, they are separate concerns.
- Modern component frameworks are bad, because they break this rule.
Now, let me preface this discussion by noting that it specifically applies to web application development.
If youâre not building a highly interactive application, in a component-driven way, your concerns might be different than the ones described here.
With that out of the way, are the concerns of Structure, Style, and Interactivity really separate?
Components in other languages
If one were to design a language from scratch today, whose purpose is to build interactive applications, would they divide it into three? One language to create the structure of the component, another to style the component and one more to define its behavior?
I doubt anybody would think this is a good idea. Sounds pretty damn dumb if you ask me.
Yet, we find people claiming that this is how web apps should be built, supporting their argument with the fact that HTML, CSS and JS are three different languages, thus three different âconcernsâ, as if oblivious to the historical context of how web technologies came to be.
The web is HTML, CSS and JavaScript because it evolved from a read-only document sharing system, that later gained its styling and interactive capabilities, not because those technologies are the optimal UI development paradigm.
Letâs use a modern example by looking at SwiftUI, a somewhat recent approach to building user interfaces. Hereâs some code pulled from their docs:
Button(/* */) {
Label("Refill Water", systemImage: "arrow.clockwise")
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity)
.padding(.vertical, 8)
.padding(.horizontal, 12)
.background(.quaternary, in: .containerRelative)
}
Youâll notice that the notion of separating styling from the component structure is absent here.
Now, Iâm not pointing to SwiftUI as the definitive source for UI development. My own experience indicates that this approach is more efficient than separating structure and style.
Iâve seen monolithic CSS files climb to thousands of lines, and methodologies such as BEM to try and rein in the chaos, but nothing is as simple and elegant as component-scoped styles.
Presentation as a specific concern
Different frameworks handle styling differently, but most embrace the idea of component encapsulation.
Having external styles impact your components in a cascade, originating from outside to dictate the appearance of child elements makes reasoning about, and debugging styles more challenging, requiring the developer to constantly keep extra context in mind.
If you find yourself asking âWhere is this CSS coming from?â, thereâs an issue with your architecture. Your concerns are so well separated that you have no clue whatâs going on.
HTML, CSS and JS serve the same concern: presenting data. This is why frameworks like Vue and Svelte bundle them into single-file components.
While React doesnât prescribe a styling approach, and uses JSX rather than SFCs, the popularity of CSS-in-JS and Tailwind is undeniable.
Style encapsulation at the component level is more efficient and easier to work with, especially as the component library grows.
Letâs take a look at an example, this time using Theme UI, described as âa library for creating themeable user interfaces based on constraint-based design principles.â
To keep things simple, Iâm pulling the example from their front page:
// Create your theme
import type { Theme } from 'theme-ui'
export const theme: Theme = {
fonts: {
body: 'system-ui, sans-serif',
heading: '"Avenir Next", sans-serif',
monospace: 'Menlo, monospace',
},
colors: {
text: '#000',
background: '#fff',
primary: '#33e',
},
}
// Style your UI
/** @jsxImportSource theme-ui */
import { ThemeUIProvider } from 'theme-ui'
import { theme } from './theme'
export const App = () => (
<ThemeUIProvider theme={theme}>
<h1
sx={{
color: 'primary',
fontFamily: 'heading',
}}
>
Hello
</h1>
</ThemeUIProvider>
)
Notice how style information is defined as data in the theme, which is then used in the JSX template via the sx
prop.
Again, Iâm not advocating for a specific library here; Iâm using this example to illustrate that web technologies collectively address the same presentation concern, which includes both structure and style.
Conclusion
In the era of component-based UI frameworks, structure, style, and interactivity are not individual âconcernsâ that should be separated. They collectively serve the same fundamental presentation concern.
Web technologies evolved into what they are today to bring new capabilities to the platform. We shouldnât limit ourselves to a dogmatic separation of those technologies that idolizes a perceived purity over practical efficiency.
Good abstractions should be discovered, rather than created. As you work in a codebase, its different concerns become readily apparent. In a web application, they may be things like data fetching, state management, and business logic, not the artificial boundaries between HTML, CSS, and JS.
So, the next time you hear someone mention âseparation of concernsâ as if the phrase itself were an argument, feel free to challenge them and ask what concerns theyâre trying to separate. Chances are they havenât put much thought into it.