diff --git a/apps/docs/blog/2024-10-10-designing-for-composition.mdx b/apps/docs/blog/2025-01-10-designing-for-composition.mdx similarity index 51% rename from apps/docs/blog/2024-10-10-designing-for-composition.mdx rename to apps/docs/blog/2025-01-10-designing-for-composition.mdx index 8c1bc5a93..e6ddd786f 100644 --- a/apps/docs/blog/2024-10-10-designing-for-composition.mdx +++ b/apps/docs/blog/2025-01-10-designing-for-composition.mdx @@ -12,96 +12,99 @@ tags: - design --- -## Style Composition has never been easy +A previous article discussed how StyleX was initially created to address the +needs that arose during the facebook.com rewrite. However, early on, StyleX +looked quite different from what it is today. -Style composition has never been easy with CSS. Applying multiple CSS rules -to the same HTML element usually leads to a constant battle with CSS order, -selector specificity, and growing CSS files. +StyleX was developed to replace our previous styling solution, which was +similar to CSS modules. The biggest challenge we faced was the massive amount +of CSS and the style recalculation costs of lazy loading styles. So, the very +first version of StyleX generated atomic classes. -CSS has evolved to provide additional tools such as `@layer` which makes -the problem slightly better. However, the problem persists and the responsibility -for solving the problem remains on tools such as StyleX. +In fact, that was *all* StyleX did for some time. We used utilities like +`classnames` to compose styles just like we did with our previous styling +system. The API looked quite different back then: -## Style Composition is desirable +```tsx +const styles = stylex({ + root: { + color: 'white', + backgroundColor: 'blue', + } +}); -Despite massive challenges with style composition, developers have always -wanted ways to compose styles. Even before CSS-in-JS libraries became -popular, developers used packages such as `classnames` to try and apply -multiple sets of styles to the same element, despite all of the challenges -that come with it. +
+``` -In any design system, it is common to modify or override certain styles -of components within certain contexts. It is also useful to be able to use -component libraries and customize them to fit the design of the application. -The alternative leads to unnecessary duplication of styles and code. +It was impossible to use `styles` outside of the file they were defined in. -The rise of runtime CSS-in-JS libraries was fuelled, in part, by the need -to solve the style composition problem. By being able to generate and inject -styles at runtime, these libraries were able to provide a way to compose -styles in a way that was not possible with traditional CSS. However, this -also came with performance trade-offs. +## The Need for Style Composition -In recent years, just as we've seen a rise in the popularity of atomic CSS, -we've also seen utilities that merge such styles gain prevalence. Many tools -now offer a way to merge styles. +Initially, it seemed like style composition was unnecessary. After all, we'd been +able to get by with `classnames` to compose styles from CSS modules for years. +But as we built a component library on top of StyleX, we realized that we *needed* +style composition. We needed the ability to pass styles around as props +to components. It became clear that style composition was already +pervasive in our codebase. However, style composition with `classnames` was +inconsistent and unpredictable, and we had learned to accept it. -Despite all the tools, style composition remains a challenge. From headless -UI libraries to design system components that are copied to your own codebase, -we believe many of these tools need to exist entirely because of the inconsistencies -of merging styles. +Style composition has never been easy with CSS. Applying multiple CSS rules +to the same HTML element usually leads to a constant battle with CSS order, +selector specificity, and growing CSS files. CSS has evolved to provide +additional tools such as `@layer`, which makes the problem slightly better. +However, the problem persists. -We believe that we've built the first solution with style composition at its very core. +In any design system, it is common to modify or override certain styles +of components within certain contexts. Component libraries need to be customizable +to fit different application designs. Without proper style composition, we end up +with unnecessary duplication of styles and code. -## Inline Styles have always been composable +The rise of runtime CSS-in-JS libraries was fueled, in part, by this need +to solve the style composition problem. By being able to generate and inject +styles at runtime, these libraries provided a way to compose +styles in a way that was not possible with traditional CSS. However, this +also came with performance trade-offs. -Despite the challenges of composing styles with CSS, inline styles have always -been composable. In HTML, `style` accepts a string with a list of styles. In -this string, *the last style applied always wins*. +## Learning from Inline Styles -Inline styles have their own limitations, too. They don't support pseudo -classes, media queries, or other selectors. It is also difficult to enforce -consistency when using inline styles directly in HTML. +While exploring solutions, we made an interesting observation: inline styles have +always been naturally composable. In HTML, `style` accepts a string with a list +of styles. In this string, *the last style applied always wins*. -However, component-based frameworks, such as React, largely sidestep any -architectural issues with inline styles. Components are a layer of abstraction -that enables code reuse without needing to write the same styles over and over -again. This change was noticed early and was described in the original -[`CSS-in-JS` talk by _Christopher Cheadeu_](https://vimeo.com/116209150). +Inline styles have their own limitations, too. They don't support pseudo-classes, +media queries, or other selectors. It is also difficult to enforce +consistency when using inline styles directly in HTML. However, component-based +frameworks, such as React, largely sidestep any architectural issues with inline +styles. Components are a layer of abstraction that enables code reuse without +needing to write the same styles over and over again. This change was noticed +early and was described in the original [`CSS-in-JS` talk by _Christopher Cheadeu_](https://vimeo.com/116209150). So, when it came to designing StyleX, we decided to model our styles after inline styles. To form a mental model, it can be helpful to think of StyleX as "inline styles without the limitations". -StyleX gives you the ability to use capabilities such as pseudo classes and -media queries, which are not possible with inline styles, all while maintaining -the ability to compose styles in the same way that inline styles do. +## Key Design Decisions -## Do "inline styles" need to be defined inline? +### Static Style Definitions -By the time we made the conscious decision to model StyleX after inline styles, -StyleX had already been in development for a while and had evolved organically -inspired by React Native's `StyleSheet` API, which was itself inspired by inline -styles. +By the time we made this conscious decision, StyleX had already been in development +for a while and had evolved organically, inspired by React Native's `StyleSheet` API, +which was itself inspired by inline styles. -And so one of the first design decisions we reconsidered was the requirement to +One of the first design decisions we reconsidered was the requirement to declare `stylex.create` before using it, without allowing the definition of styles -inline. There are trade-offs to both approaches, we realised, that we *had* to -allow the ability to statically define styles as JavaScript constants and be -able to reuse them across multiple elements, multiple components, and -even multiple files. Once we had this realization, we felt more comfortable -not offering the ability to define styles inline. Even if occasionally inconvenient, -it is better to have *one* consistent way to define styles. +inline. We realized that we *had* to allow the ability to statically define +styles as JavaScript constants and be able to reuse them across multiple elements, +multiple components, and even multiple files. Once we had this realization, we +felt more comfortable not offering the ability to define styles inline. Even if +occasionally inconvenient, it is better to have *one* consistent way to define styles. -The requirement to define styles at the top-level of a module also enforces some of -the requirements of the compiler without feeling *too* unnatural. +### Pseudo-Classes and Media Queries - -## Pseudo Classes and Media Queries - -Our next design question was to decide how to handle pseudo classes, media queries +Our next design question was to decide how to handle pseudo-classes, media queries, and other at-rules in a way that *felt* like inline styles and enabled composability. -Inline styles don't support pseudo classes or media queries, but it's possible -to use JavaScript to read such 'conditions' and apply styles accordingly. +Inline styles don't support pseudo-classes or media queries, but it's possible +to use JavaScript to read such 'conditions' and apply styles accordingly: ```tsx