Replies: 1 comment
-
|
We're moving in the same direction with components. The capture tag is a great swiss-army-knife but it has limitations. Some real patterns for slots would make our code simpler. |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
In the Shopify theme ecosystem, we’ve seen a pattern emerge where single-use abstractions are created to slot content in specific scenarios. Examples include:
{{ content_for_additional_checkout_buttons }}{{ content_for_header }}{{ content_for_index}}{{ content_for_layout }}{{ content_for: "blocks" }}These Shopify-only Liquid tags serve their original purpose but ultimately lack flexibility, so more and more are created.
Archetype Themes is striving towards component-based architecture where snippets are more than just isolated pieces of markup—they should be dynamic, modular, and easily reusable across different parts of a theme and theme projects. See Intro to Theme Components for more details.
The attributes of component-based architecture are highly aligned with the objectives expressed by the Theme Blocks / OS3.0 project.
Currently, snippets are almost fully equipped to support an MVP of component-based architecture. However, the lack of native slotting capabilities in Liquid is the biggest limitation, preventing effective component nesting and composability.
The problem with Theme Blocks for composability
Theme blocks introduce a form of slotting via the
{{ content_for: "blocks" }}that currently allow for a single slot to be declared in a chunk of code, and its contents are determined by either a preset json config or the theme editor data saved intemplates/*.jsonfiles.A principal we're leaning into at Archetype with theme components is clear separation of presentation from state. To keep liquid code reusable across as many theme projects as possible, it needs to purely consume state, not also try to define it. State objects like theme settings and locales and feature configurations are too variable from project to project -- they can't be tightly coupled to presentation.
With this separation of presentation and state, we can approach the theme editor as a progressive enhancement and selectively define what parts of the presentation we want to be configurable by merchants in the theme editor, and what we want to be configurable privately within the theme code. Maybe in this instance of using a button component we want to make button roundness configurable, and then maybe in this other instance we do not.
The problem with Theme Blocks is that it is introducing slotting and composability, a core presentation layer capability, in the same place where state it defined. And it's doing so in a Shopify Theme Editor only environment, not within Liquid itself where it could be used more broadly.
The outcome of the current approach can be seen in the reference-theme. Consider the case of the link block.
There are cases where a theme developer will want to provide flexibility of where a link is positioned and how it is styled, like in the slide block and other cases when they because configurability here will add unneeded complexity, like in the product-card. Two versions of the same code are needed here to handle these different scenarios, one that is private and one that is configurable in the editor.
Native first, theme editor second
If we follow the principals of clear separation of presentation from state and the theme editor as a progressive enhancement, then slots should be available natively in Liquid and then Theme blocks can be the progressive enhancement that selectively expose slots for configuration in the editor.
Right now, snippets have some degree of slotting capability in a static sense. You can capture content and slot it into a snippet as a parameter.
But this approach is limited by the lack of reference passing, which hinders the true composability of components.
For instance, it’s not possible to nest a product-card snippet inside an item-grid snippet and have the item-grid snippet dynamically render multiple instances of the product-card snippet within a loop. How nice would it be to be able to do the above example like this (purely illustrative and not suggestive of final syntax):
The ideal solution would be to introduce something akin to HTML Slots, where snippets could have native slotting capabilities, allowing for true dynamic nesting and composition. I've provided some test assertions and comparisons between Liquid and HTML slots in the collapsible below:
Core Test Assertions of Slotting in a Language: Liquid vs. HTML Slots
When evaluating the core test assertions of slotting in a templating language, HTML slots provide a strong basis for comparison. Below are the key assertions, along with whether they pass or fail in Liquid:
Basic Content Insertion (Passing Static Content into a Slot)
• Assertion: A component should be able to accept and render content passed to a defined slot.
• HTML Slots: elements in a Web Component accept and render content passed by the parent.
• Status in Liquid: Pass. Liquid allows static content to be passed into a snippet through parameters, and it can render this content within the snippet.
Dynamic Slot Content (Passing Dynamic Content into a Slot)
• Assertion: A component should be able to accept dynamic content (e.g., variables or expressions) passed into a slot.
• HTML Slots: Dynamic content can be passed to and rendered within slots.
• Status in Liquid: Pass (with limitations). Liquid can accept dynamic content through variables passed as snippet parameters, though it is less flexible than other templating languages in handling complex expressions or conditional logic within slots.
Default Slot Content
• Assertion: A component should provide default content for a slot if none is provided by the parent.
• HTML Slots: elements can include fallback content that is displayed if no content is provided by the parent.
• Status in Liquid: Fail. Liquid does not natively support default slot content; you would need to implement conditional logic manually within the snippet, which is cumbersome and less elegant.
Multiple Named Slots
• Assertion: A component should support multiple named slots, allowing different content to be inserted into each slot.
• HTML Slots: Multiple elements with name attributes can accept different content based on the slot’s name.
• Status in Liquid: Fail. Liquid does not natively support named slots. While you can pass multiple parameters to a snippet, they do not function as true named slots, and managing multiple content areas within a snippet requires custom logic.
Slot Composition (Nesting Slots)
• Assertion: A component should be able to nest slots, allowing one slot to include another component or slot within it.
• HTML Slots: Slots can contain other Web Components or slots, allowing complex nested content structures.
• Status in Liquid: Fail. Liquid does not support dynamic nesting of snippets within slots in a way that allows for true composition. You cannot dynamically nest a snippet within another snippet’s loop or slot in the same way as HTML slots allow.
Slot Scope (Accessing Parent Scope within a Slot)
• Assertion: A slot should have access to the parent component’s scope, allowing it to use variables or functions from the parent.
• HTML Slots: While HTML slots do not inherently support scoped access, scoped slots are a concept in other templating languages.
• Status in Liquid: Fail. Liquid snippets do not support scoped slots; snippets operate in isolation and cannot access the parent’s scope in a dynamic way. Data must be explicitly passed to the snippet, limiting flexibility.
Dynamic Slot Selection (Conditional Slot Rendering)
• Assertion: A component should allow conditional rendering of different slots based on certain conditions.
• HTML Slots: Although less common in native HTML slots, dynamic slot selection can be handled with JavaScript.
• Status in Liquid: Fail. Liquid does not natively support conditional slot rendering. While you can implement conditional logic within a snippet, it’s not as straightforward or dynamic as with other systems.
Slot Fallback and Overriding
• Assertion: A slot should allow content to be overridden by the parent, but provide fallback content if none is provided.
• HTML Slots: elements can include fallback content that gets overridden if content is provided by the parent.
• Status in Liquid: Fail. Liquid doesn’t support fallback content within slots or the ability to easily override slots. Any fallback logic has to be manually coded.
Conclusion
I hold a strong opinion that core Liquid functionality like slotting shouldn’t be something that’s only made possible through the Theme Editor. The Theme Editor should be a tool for progressively enhancing what’s already possible with Liquid, not the gatekeeper of fundamental capabilities. By introducing native Liquid slots, we can take a meaningful step towards component-based architecture in Shopify themes, making our code more modular, maintainable, and reusable.
Additional context + discussions
Beta Was this translation helpful? Give feedback.
All reactions