Catalyst Themes Discussion #2303
Replies: 1 comment 1 reply
-
|
I'll first caveat my thoughts that I'm not actively working with Catalyst, so others will have more hands-on experience with this problem. Lots to digest in your post but I'll focus on the sections query as that's the main one right now. Section as the APIOne thing that struck me from the model you describe, where the theme "API" is defined at the route <-> section barrier, is that it does feel very restrictive. However, it does feel like the right place to divide responsibilities between the framework and the UI. I like that it has a parallel to stencil theming where the delineation is at the route/template boundary. Then, as a developer, you have free rein to change what you like within the template file. You would hope for a composable approach that you would have at least this much flexibility, if not more. When it comes to a section's interface, I wonder if, in addition to the route providing the appropriate context to the section, all sections all support a Sections would then be: And could be registered like: Thanks for the thorough post @matthewvolk and for opening it up for a conversation! |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Today, Catalyst offers no first-class theme development model. Instead, if you are a developer who wants to ship a custom look for a Catalyst storefront, you are left to fork the entire Catalyst codebase, rip out or modify styles and components in disparate files, and maintain that fork as Catalyst evolves.
Forking Catalyst might be the most flexible option for ensuring that you have full creative control over how your custom theme looks and functions. If you don’t want to use our existing design system (VIBES), you can swap it out for something else entirely, like Material UI. If your custom component does not have the exact data it needs from the BigCommerce Storefront API, you can go in and modify the code to request the data you need yourself.
However, while forks provide great flexibility, they are also extremely cumbersome to keep up to date—and if your fork is not up to date, it may dissuade users from wanting to use your theme for their production storefront.
The purpose of this discussion is to collect feedback from you, our community of Catalyst developers, on what the future of Catalyst theming should look like. Since Catalyst is open source, we believe we should build as transparently and collaboratively as possible—so, if you are a developer interested in building, listing, and selling a custom theme for Catalyst, we would love to hear your opinions on the exploration we’ve done below.
Context
It’s worth stating that the concept of a “theme” isn’t well defined in the context of composable commerce starters such as Catalyst. Should Catalyst Themes look more like “accelerators”, where a developer forks Catalyst and heavily modifies its functionality to serve a very specific industry vertical/use case? Or, should themes only be concerned with frontend styling and behavior?
We’ve taken one approach below that leans more towards the latter, but the purpose of this discussion really isn’t to recommend or show preference for a single approach. In fact, we hope to focus more on the potential solution’s drawbacks, as they may highlight potential design flaws that could become hard to change in the future, or could even signal that themes might take a different shape than what we have in mind.
Potential Solution
Tip
This section references code available in GitHub on a branch called
theming-poc. We’ll link out to particular files and lines of code throughout this section for you to make it easier for you to follow along.We recently took some time to audit which components are being imported throughout the entire
app/directory nested insidecore/. We noticed that most of the import statements referencing UI code were importing components from the “sections” directory insidecore/vibes/soul/sections.These “sectional” components (such as
FooterorProductDetail), as well as their prop interfaces, essentially function as the “public API” for our component library. The components themselves define the UI that Catalyst depends on, and the interfaces for those components tell the components what data Catalyst makes available for them to render. These larger sectional components also do a good job of encapsulating all of the UI (CSS, pending/error states, etc.) needed to style the component within the component itself. We spent some time exploring this concept as a potential framework for theme development:💡 What if a theme was simply a list of UI components that you were required to export, as well as some associated interfaces that you were required to implement within those components?
In the branch above, we started working down this path. We created a single top level
core/uicomponent for each of the sectional components imported by the Next.jsapp/directory. Take for example,core/ui/featured-product-list/index.tsx:catalyst/core/ui/featured-product-list/index.tsx
Lines 33 to 42 in dd42e42
This file exports a single component called
FeaturedProductList. However, rather than exporting that component from VIBES, you can see I'm exporting it from my own custom theme component that lives in~/ui/my-theme-components/featured-product-list:catalyst/core/ui/my-theme-components/featured-product-list.tsx
Lines 4 to 26 in dd42e42
Whether your
FeaturedProductListcomes from VIBES, or your own custom component built with whatever UI design system you prefer, as long as yourFeaturedProductListcomponent implements theFeaturedProductListPropsinterface, the Next.jsapp/directory will still be able to satisfy its import requirements wherever it is used, such ascore/app/[locale]/(default)/page.tsx:catalyst/core/app/[locale]/(default)/page.tsx
Line 10 in dd42e42
catalyst/core/app/[locale]/(default)/page.tsx
Lines 54 to 61 in dd42e42
Of course, this is a very rudimentary example; there is much cleanup work we’d want to do to ensure the interfaces and even the actual components are reasonable enough for us to call them “required”. If we go down this path, we’d want to work closely with the community to ensure the interfaces have all of the data that developers need to build complete components.
This model would be easy to standardize; as long as you create and export components for each of the top level
core/uidirectories, and those components implement each of their associated interfaces, the theme could be considered “complete” (at least from the perspective of theapp/directory). Theme documentation could even start out as simple as a README.md file included in your fork’score/uidirectory.This model is also flexible. Maybe you’d rather use Tailwind Plus, or a completely different design system like Material Design + Material UI, or maybe you’d like to build your own design system! As long as you export all of the required components + interfaces, you have the freedom to build using whatever technology you’d like (this is why in #2247 we propose renaming the
core/vibesdirectory tocore/ui—you can think of the Soul VIBE as simply the default implementation of a Catalyst theme, similar to Stencil’s Cornerstone).Finally, this model is more maintainable. If we go with this approach, all of your theme code could feasibly live in a single directory (
core/ui), meaning merge conflicts would become much more manageable. Breaking changes would be communicated via changelogs, and we would consider a change to the interface (both a change to theindex.tsxcontents and a change to the name of the file directory paths) a breaking change.Drawbacks
As mentioned in the introduction, we found a number of potential drawbacks with this potential solution above.
The largest drawback comes when you find a need to extend an interface with data that it doesn’t already have access to. For example, the
Cartsection only has access to data for the cart contents, coupon codes, and shipping information:catalyst/core/ui/cart/index.tsx
Lines 85 to 96 in dd42e42
What if you wanted to inject the store’s best selling products into the cart section? You wouldn’t be able to unless we release a new update to the interface for
Cart—but that requires theme developers to wait for these updates each time they have a component that needs new data, which is extremely inefficient. If we take too long to release an update, developers would have to resort to reaching outside of theuidirectory to modify the data fetched inside the Next.jsapp/directory, bringing us right back to where we are now. This is a pretty major shortcoming from a flexibility perspective; theme developers would have no way of declaring new data for their components to render without stepping out ofcore/ui. We thought a little bit about how we could enable declarative data fetching inside your UI components, with a pattern similar to dependency injection… but what exactly does dependency injection look like in the context of idiomatic React code? If we go down this route, we’d want to find a solution that plays nicely with React best practices and is easy for developers to quickly pick up and adopt.Another obvious drawback is that while the surface area of your custom code has shrunk into a single directory,
core/ui, you are still maintaining a fork of Catalyst. There is a chance that we land on a Catalyst theme development model that still relies on forks. As we mentioned before, we agree that maintaining a fork of Catalyst is not an easy process. We know this from maintaining theintegrations/makeswiftbranch—so, regardless of the path we go down, we plan to invest in tooling and standards to make it easier to keep forks up to date. In some of our latest PRs, you’ll notice we included a new section for migration steps. Internally, we’re discussing ideas for CLI tooling that makes it easier to deal with introducing updates to your fork without all of the merge conflicts that are sometimes associated with those updates. Forks are the primary way we recommend getting started with a Catalyst codebase, and we don’t feel satisfied with our current approach for tooling to keep those forks up to date.Currently, our existing UI library (VIBES) has a public API that is tied to various external libraries. For example, some of our interfaces reference the
SubmissionResulttype from Conform, or theStreamableutility library bundled with VIBES. We’d have to think about how we balance flexibility in theme development with best practices for each of these cases. You’ll notice that localized strings make up a large part of the current set of interface definitions; we’ll need to provide some guidance on how a theme could provide translation dictionaries, because it’s possible that different themes will have different strings for different components.There are, of course, a number of other open questions that would need to be addressed if we continue down this path: How will we alert theme developers that an interface has changed and their theme needs updating? Should themes be required to ship with Makeswift bindings by default? How will storefront developers “install” your theme? These questions are indeed important, but should only be investigated if we as a community decide to go down this particular path for themes.
Closing Thoughts
We’ve anchored on a few concepts throughout the solution we described above: flexibility, standardization, and maintenance. While it is true that we aren’t sure of the final shape that themes might take in the future, we do think that a robust model for Catalyst themes should have at least those three qualities.
In closing, we’ve explored one shape that themes may take in this discussion post; this shape focuses more on the frontend styling aspect of Catalyst. But, we’d love to hear your opinions about why this path would or would not work for you or your use cases. Are you interested in an alternative shape that we may not have even considered? Please let us know in the replies below!
Beta Was this translation helpful? Give feedback.
All reactions