Overview
Observations:
- We need to allow SDCs to specify they accept HTML markup in a particular prop.
- We also need to allow using CKEditor 5 for (some but not all of?) those props.
- It is currently not supported. A plain
type: stringis interpreted as\Drupal\experience_builder\Plugin\Validation\Constraint\StringSemanticsConstraint::PROSE, because that is by far the common scenario.
Proposed resolution
- 👎 Introduce a new
type: string, format: markup— problem: this is non-standard: https://json-schema.org/understanding-json-schema/reference/string#built... - ❌
🤔 Useper @lauriii in #6type: string, pattern: ^\<.*\>$to signal this, i.e.: "this string must start and end with an angular bracket. This is simple, pragmatic, and unlikely to clash with other needs. - ❌
Usetype: string, format: markupdespite it not being in the standard. Drupal/XB can/could/should define it because it is a critical capability. Per https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validati..., it is definitely the intent of the JSON Schema spec to allow additional formats (and e.g.\JsonSchema\Constraints\FormatConstraintallows for this too).
To allow expressing the specific HTML restrictions, we useenum, to list detailed restrictions as the existing\Drupal\Tests\ckeditor5\Unit\HTMLRestrictionsTest::testRepresentations()infrastructure + test coverage already proved to be viable.
For
that would then for example beheading: title: Heading type: string format: markup enum: - <em> - <strong> examples: - The future of <em>something</em> explanation: title: Explanation type: string format: markup enum: - <p> - <br> - <a hreflang href> - <ul type> - <ol start type> - <strong> - <em> cta_button_label: title: Call To Action type: string - ✅ Based on #30, I think that this implementation sequence would be viable:
- By end of Q2:
StaticPropSources usingtextfield type using CKEditor 5 - Implement @lauriii's simple subset ("inline and block") (bottom of #23), based on @effulgentsia's proposed syntax (#30 + #40). That'd result in SDC props that specify either
-
heading: type: string contentMediaType: text/html x-formatting-context: inline -
explanation: type: string contentMediaType: text/html x-formatting-context: blockor, equivalent:
explanation: type: string contentMediaType: text/html
(This example matches
.)This uses JSON Schema's
contentMediaTypeas well as JSON Schema's custom annotations usingx-…, both surfaced by @effulgentsia (👏) in #30. It also uses CSS' existingformatting contextterminology.Note that this metadata nicely also conveys the necessary information to know how to offer in-place editing in the future (see #3453690: [META] Real-time preview: supporting back-end infrastructure):
x-formatting-context: inlinewould cause CKEditor 5's inline editor to be used, andx-formatting-context: blockwould cause CKEditor 5's balloon editor to be used.- For
x-formatting-context: inline, we'd use axb_inlinetext format (restricted to<strong> <em> <u> <a href>) configured to use CKEditor 5 (with Bold, Italic, Underline and Link buttons). - For
x-formatting-context: block, we'd use axb_blocktext format (restricted to<strong> <em> <u> <a href> <p> <br> <ul> <ol> <li>) configured to use CKEditor 5 (with Bold, Italic, Underline, Link, Bulleted list and Numbered List buttons).
Both use only
filter_htmlfilter plugin to capture HTML restrictions. Zero other filter plugins are used. This ensures that it's not necessary to perform filtering (which can happen only on the server) when typing in the CKEditor 5 instance, hence allowing for true real-time previews! (Theoretically this imposes a security risk, but in reality this only allows self-XSS if the user manages to type a tag not listed, that CKEditor 5 then also renders. For end users, i.e. when not editing components through XB and performing this pure client-side update of the preview, the actual filtering process would still occur.)We'd decorate the
FilterFormatandEditorconfig entity types' storage handlers to hide these 4 config entities, because neither config entity type supports (un)locking.⚠️Big caveat: IIRC there are problems/limitations around CKEditor 5's ability to be configured to use only inline elements — IIRC
<p>and<br>are fundamentally assumed to be allowed.
-
- By end of Q2:
DynamicPropSources usingtextfield type using CKEditor 5 - The above only solves the
StaticPropSourcecase, but given #3455629: [PP-1] [META] 7. Content Templates — aka "default layouts" — affects the tree+props data model is also moving forward, we should assume that it needs to be possible to maptextfields (aka structured content)'s HTML markup, too.To do this, we need to be able to identify which fields contain markup. We already have that:
StringSemanticsConstraint::MARKUP, introduced in April 2024, before XB even used d.o.What we don't have yet, is matching the SDC "HTML prop" constraints against that of the text stored in the text field.
The challenge is that 99% of text formats do allow block-level elements, hence making them incompatible withx-formatting-context: inline. Would it be okay to never be able to match structured content (i.e. data in atextfield) against an SDC prop with the schematype: string, contentMediaType: text/html, x-formatting-context: inline? 🤔
@lauriii suggested a reasonable work-around: XB should match aDrupal \Drupal\Core\Field\Plugin\Field\FieldType\StringItemfield against atype: string; x-formatting-context: inlineSDC prop.The remaining challenge: these fields can use arbitrarily complex text formats and editors. Both bring challenges:
- Some editor configurations may trigger Drupal-rendered modal dialogs, which likely need extra work (see: all the work to make the Media Library Widget work in XB).
- Most text formats use multiple
filterplugins, not just thefilter_htmlfilter. They virtually all require a round trip to the server:filter_image_lazy_load,filter_captionetc. all areTYPE_TRANSFORM_REVERSIBLE-type filters.For these text formats, typing in the CKEditor 5 instance will require a round trip to the server to render a preview, so "true real-time updates
will be impossible.Note that all this is a solved problem: Drupal 8 core's Quick Edit module tackled exactly this. It's been ejected out of core, but lives on in contrib: https://drupal.org/project/quickedit. XB will be able to reuse the infrastructure it added, and will only need a subset of it.
- Follow-up: later: allow customization
- Allow customizing the
xb_inlineandxb_blockFilterFormat+Editorconfig entities, by making them visible in the UI like all others, but provide warnings and confirmation dialogs. Add validation logic that forbids adding more filters to the text format, because that would introduce a dependency on the server, hence breaking true real-time previews.(Until this is implemented, if ever, the config can still be modified directly by developers.)
- Follow-up: later still: detailed customization
- In the future, we could expand this, by adding support for the
x-allowedTagsthat @effulgentsia proposed in #30.If/when that is present, we'd not use the aforementioned text formats, but instead use
x-formatting-contextsolely as a hint for how to offer in-place editing, and generate an in-memory-onlyFilterFormat+Editorconfig entity object based on those restrictions, with corresponding CKEditor 5 plugins at their default settings, and with buttons added to the toolbar if they are needed for those HTML tags (thanks to\Drupal\ckeditor5\SmartDefaultSettings::addToolbarItemsToMatchHtmlElementsInFormat().And/or alternatively: we could introduce a
x-ckeditor5whose value must be a value that passes the validation logic fortype: editor.settings.ckeditor5. That would allow complete control by the SDC developer, at the cost of very complex metadata.The point of all this: thinking through what it'd mean to go much further in allowing SDC developers to specify the authoring experience for Content Creators.
- By end of Q2:
User interface changes
CKEditor 5 is available in the right sidebar for some component props!
- All 6 permutations of "HTML prop shapes" appear for the
all-propsSDC: required vs optional, formatting contextblockvsinlinevs none 
- The 2 new text formats + editors are listed in the UI, but cannot be modified

| Comment | File | Size | Author |
|---|---|---|---|
| #51 | Screenshot 2025-04-04 at 1.29.11 PM.png | 178.26 KB | wim leers |
| #51 | Screenshot 2025-04-04 at 1.02.19 PM.png | 525.5 KB | wim leers |
| #47 | 2025-04-02_12:10:12.png | 26.28 KB | salmonek |
| #47 | 2025-04-02_12:09:33.png | 20.92 KB | salmonek |
| #37 | 3467959-37-shape-matching.patch | 3.85 KB | wim leers |
Issue fork experience_builder-3467959
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
effulgentsia commentedFrom the perspective of an SDC author, what's the difference between an HTML prop and a slot? What are example use-cases for an HTML prop and why shouldn't that be a slot instead?
If we decide to go with this option, I suggest
format: htmlrather thanformat: markup.Not all HTML fragments start and end with brackets. E.g.:
hello world<em>hello</em> worldhello <em>world</em>are all valid HTML fragments.
Comment #3
wim leersFair questions. I’ve seen @lauriii state that this should be a prop, not a slot. So I considered that not a question to ask, but a given.
This is already assigned to Lauri to get clarification on how this should work, now there’s just more to explain his vision for. 👍
Comment #4
wim leersComment #5
wim leersImportant: at least the current design does include a rich text editor/CKEditor 5 instance:
Comment #6
lauriiiI was imagining this would be a prop because a) it's not any HTML that the prop allows, it's HTML generated by a rich text editor that's allowed b) slots are considered separate from props which would make generating a form pretty complex.
What comes to configuring the prop, it seems that
type: string, pattern: ^\<.*\>$is also pretty bad for developer ergonomics, and thattype: string, format: markupor type: string, format: html would be much better from that perspective.Comment #7
effulgentsia commentedDoes it make sense for the SDC creator to decide that? A rich text editor can generate almost any HTML (e.g., if you use Drupal's "Full HTML" format), so what exactly is the decision that the SDC creator is making when saying something is an HTML prop vs. a slot? I.e., imagine someone creating a component library in Storybook and not thinking about Drupal concepts: for this person, what does it mean for something to be a slot? what does it mean for something to be an HTML prop?
Comment #8
wim leers#6:
Agreed it'd be better.
But it's not part of the JSON schema spec: https://json-schema.org/understanding-json-schema/reference/string#built... — we'd have to define this ourselves. And if then define it in some particular way, that brings me to #7:
#7++ — that's the part that's fuzzy to me too: what is the intent? What constraints does an SDC developer want?
Comment #9
wim leersComment #10
lauriii#7: The SDC schema is not generic in the sense that other systems would be able to read it without a specific Drupal integration. In the process where you have pre-existing components that may exist in a component library utilizing storybook, you would still have an integrator specifying the SDC schemas for the Drupal integration, which is when these types of decisions would be made. In Twig there are some interesting aspects to this, such as that slots have their own syntax, meaning that some of these decisions may be made upfront. I believe it is possible to expose a slot as a prop within a component without modifying the original code but not vice versa.
I think using either props or slots could be right depending on the use case. Here's two examples that demonstrate this.
Example 1:
Let's imagine a frontend developer received this as a design for them to implement as a component. Because the text is using bold and italic, would they think automatically that they should be using a slot that could allow variety of inputs? I personally don't think so because this would always be a short text that could have some formatting. Because HTML is somewhat arbitrary and generally wouldn't be used for defining logic, they could potentially define this as a slot that only allows a paragraph component but that would mean that the content is authored via a separate component.
Example 2:

Let's imagine we now have this as a design for them to implement as a component. The text has formatting but based on this design, we can expect this layout to also allow content creators using elements other than the text. We can se an example of button being shown in the design. I think in this case it would most likely make sense to use slots that provide content creators with more flexibility to use the space.
Comment #11
lauriiiComment #12
pdureau commentedHi all,
As effulgentsia asked: "Does it make sense for the SDC creator to decide that?"
In my opinion, no, it doesn't. The component author doesn't have to care about rich text editors and other CMS stuff.
The component author is building a pure UI data model, without any applicative or business considerations, where he needs to pick the right type for each data he is expecting in the template.
If there is markup, it is a slot. I would advice to keep
stringprop for plain text.Comment #13
lauriii@pdureau I think we might be talking past each other because I think you could still define component libraries and design systems that are free of business considerations. I could imagine with the example that I provided in #10 that organizations with more experience in design systems could own two components; one that's free from business considerations and one that includes business considerations. Only the one that includes business considerations would be exposed to content creators within XB. An organization could decide to share either one or both between sites depending on your particular needs. What I'm arguing is that frontend developers should be able to define these constraints in a way that is natural to them, which would be in
*.component.yml.Comment #14
pdureau commentedHi Laurii,
Indeed, our comments have crossed in the thread.
Thanks for those interesting examples.
The prop type described in the issue will also allow users to inject any markup, like an other component markup.
Of course, you can prevent this in the Experience Builder UI, by allowing only a simple WYSIWYG for this prop type, but it doesn't change the reality of the component, and the other usages (custom PHP code, twig includes, other display builders...) it will get. And the component author may not even know what Experience Builder is. It is like using a Field Widget instead of a Field Type to define and check the field's stored data.
If a component author really want to enforce this specific design (with one line of italic, and one line of italic + bold), it becomes a part of the component itself. So, 2 string props, one for each line, seems to be a good solution.
I am not sure I am understanding this idea. The business-related UI component will call/embed the business-agnostic UI component ?
Comment #15
lauriiiYes 👍 It could look like something like this:
The business agnostic card in the design system which would work in XB, but would not result in optimal editorial experience for a content creators:
A site specific card component with business requirements accounted for resulting in better experience for a content creator:
Comment #16
wim leers@lauriii: your response in #10 is helpful, but if both examples listed the slots + props (and their schemas) you'd expect, then it'd help guide this conversation much more. 🙏
@pdureau in #12:
This is counter to the vision @lauriii has expressed, and would violate many of the 64 product requirements.
A rich text editor is associated with "markup", and specifically "restricted markup". This is critical. As a SDC component author, I'd expect to be able to express that in a particular markup prop, only
<strong>and<em>are allowed, but in another prop only is allowed. That's what @lauriii is saying in #13:If this is to be represented by a slot, then the "per-slot tag/category-based restrictions" part becomes critically important (see [#15637196-3] and #3462705: [META] Missing features in SDC needed for XB).
@pdureau in #14:
I think this is the fundamental mismatch that's causing the misunderstanding. "just a simple WYSIWYG" is not precise enough. The prop's schema should be able to optionally convey exactly which tags are allowed. At least, that's my hunch. I'm looking to product manager @lauriii to clarify his product vision & requirements 🤓
That would indeed be the other possible solution. I'd be fine with that too. I defer to @lauriii.
I added a third proposal to the issue summary for the syntax to describe HTML restrictions. Very curious what y'all think!
P.S.: this entire discussion has a strong connection to #3446083: Document supported component modeling approaches.
Comment #17
wim leersComment #18
effulgentsia commentedI don't think this is a limitation. I think we can support in XB the concept of slots whose values are populated by the value of a rendered field type (such as
text_long) or field (such asfield_body), so that the content author doesn't see it as a separate component but rather as indistinguishable from a prop. The question then becomes where should the information live that a particular slot should be treated this way within XB.But what is the constraint that the component.yml should specify? That the slot should be constrained to "rich text" only and leave it to the system to decide what that means in terms of allowed HTML tags? Should this basically be the same as "all inline HTML tags" or would it need to vary by site or by component or be tied to Drupal's text formats?
Comment #19
effulgentsia commented#18 was a cross-post with #16/#17. If we go with the
enumapproach in Proposed resolution #3, then that addresses all of my concerns with it being a prop. Perhaps it would even make sense to name itformat: rich_textorformat:restricted_htmlinstead offormat: markupfor even more guidance as to what would typically be appropriate for theenum? Also, presumably we could create some default JSON schema definitions for commonenumsets that a component can reference via$ref?Comment #20
pdureau commentedThanks for making this explicit. However, I don't recognize here the design system philosophy and the separation of concerns I am promoting. So, I have nothing more to add than the warnings I have already shared.
I am out of this thread, but I am always happy to discuss about other SDC & XB subjects ;)
Comment #21
effulgentsia commentedIs there a reason for the SDC to constrain the attributes? I'm not clear why that would be relevant to the component's design.
If XB then uses a
text_longfield for authoring, then that field's filter format will be what restricts the attributes, but that's a separate concern from what the component cares about.This, however, raises the question of what if there's a mismatch between the component prop's allowed tags and the filter format's allowed tags?
Comment #22
effulgentsia commentedI care about design system philosophy and separation of concerns. Do you think proposed resolution #3 achieves that? As I understand it:
Or does the above not match how components for design systems are actually designed?
Comment #23
lauriiiI didn't mean this level of configurability by my comment in #13. At most, I could see there there to be a use case for two different levels of RTE for components; inline vs block (inline wouldn't wrap text in
<p>).I personally have a slight preference to the prop solution because I believe that's closer to what other platforms are doing. Pretty much all of the ones I looked at provide RTE as a unique prop type. For example:
type: 'rich-text'type: 'richtext'andtype: 'inline_richtext'type: 'richText'type: 'string', control: 'large'type: ControlType.RichTextThat said, if we find a way to provide a decent DX by using slots, I'd be totally fine with that. I'm not sure I fully understand what's the benefit of that so maybe it would be better understand what are the differences that allow it to provide a better experience.
I'm wondering if we should ship with two pre-configured text formats that are optimized to XB by us, and are always used for the XB components. The text formats can be modified but not deleted.
Block text format would come with the following buttons and would wrap text in
<p>:And the inline text format would come with following buttons but would not wrap any text in
<p>:This would allow us to optimize the experience so that the block text allows input from regular RTE fields using a token. However, the inline text would only allow input from string types.
Comment #24
kristen polBig discussions :)
Is this targeted for Barcelona?
Is the best way to see what's for Barcelona here?
https://www.drupal.org/project/experience_builder/issues/3454094
Should there be tags like "Barcelona Blocker" or similar for must haves for Barcelona?
Comment #25
effulgentsia commentedI thought more about the prop vs slot conundrum, and unfortunately I've come back around to favoring slots for this. I say unfortunately, because it will mean that this issue will take longer to get done, but I think it's worth it.
To be fair, I think there are two quite reasonable approaches for how to model the props vs slots distinction, one that leads to modeling rich text as a prop and one that leads to modeling rich text as a slot. Although both are reasonable, I think the slot approach is better.
Approach 1:
HTML web components are about the closest actual standard that exists that's closely related to the props vs slots concept. Within web components, you have attributes (which are a lot like props) and slots, and you can pass HTML content only through slots. I think this is a strong point in favor of us modeling SDC props and slots the same way. For example, it would make it easier to in the future (e.g., in #3454519: [META] Support component types other than SDC, block, and code components) support implementing SDC-like components as web components. Also, because web components are a standard, all major JS component frameworks (React, Vue, Svelte, etc.) support this way of distinguishing props and slots (even in React, where it all ends up as just props, there's docs and libraries for how to map to React props if you're coming from a web components props vs slots mindset).
Approach 2:
We can define SDC slots as things that you put other SDCs (or other kinds of components once #3454519: [META] Support component types other than SDC, block, and code components is done) into, and since content coming from a rich text editor isn't itself a component, then it should be modeled as a prop. This is the approach / mental model implied in #10, and which according to #23 is adopted by various other content management tools.
However, we don't need to adopt approach #2 in order to have the UX we want. In #3462705: [META] Missing features in SDC needed for XB we're already surfacing the need for various ways we need to express slot restrictions. Whatever approach we come up with for that, I think we can extend to also include being able to express slot restrictions as HTML tag restrictions. And if a slot restriction is expressed as HTML tag restrictions rather than as component restrictions, then we can infer that we want to give content authors a rich text editor for that instead of letting them drag other components into that slot.
Following approach 1, this would avoid the need for us to figure out how to express the concept of rich text and tag restrictions within JSON schema. Instead we can invent how to do this within the concept of slot restrictions which is something we're making up all of the syntax for anyway.
However, a downside of approach 1 is that we'll need to evolve our data model. Currently our data model consists of a clean separation between the tree (which deals with slots) and prop values (which deals with props). That would need to evolve to where the "prop values" can also include the rich text values that populate slots, at which point we might need to rename that from "prop values" to just "values".
While I don't think that extending our future implementation of slot restrictions and our current data model will add an unreasonable amount of work, I think it will likely add enough to not make it in by the 0.1.0 release that we're targeting for Barcelona. I'm not sure what that means if we want the 0.1.0 release to have the ability to populate components with rich text. Maybe there's something we can hack in as a temporary way to achieve that?
Comment #26
wim leers#18:
😨 at the SDC level? Or at the level where an SDC has been "imported" into XB? (i.e. after a
Componentconfig entity is created for it in the implementation as of today) Because tying to specific field types will require SDC developers to know about field types: bad! And allowing SDC developers to tie to specific field instances is even more concerning — especially if it's done by name.#19: 👍
#20:
That sounds very concerning 😳
I think you and @lauriii should meet to get to the bottom of this.
#21:
RE: constraining attributes — maybe that could be omitted, but I figured that some component may expect only
<ul>, no<ul type>? Or only<a href>but not<a href target>?No, we'd have to configure whichever text field to precisely match the constraints imposed by the SDC metadata. Chances of a site's pre-existing text formats nicely matching the HTML restrictions imposed by SDC authors seems slim? IOW: auto-generate a "format" in place, without it creating a
FilterFormatconfig entity.#23: "ship with two pre-configured text formats" — interesting! That'd sure simplify things. I like this a lot. That removes the very granular configurability I mentioned.
#24: yes, #3454094: Milestone 0.1.0: Experience Builder Demo is the place to look at. This should've been on the project page eons ago. Fixed that now.
We're not using tags. We'd be tagging a LOT. We're just using priority for what's critical right now, and currently, thats #3454094.
#25:
💯
👍
I don't think this is necessarily true.
First, an important question. When putting rich text (HTML markup) in a slot, is that:
Regardless of the answer to that, I think we can create a special "semi-internal"
experience_builder:rich_textSDC that gets special treatment. We place that intree, and then we can continue to usepropsas-is.+1. Less than a month prior to DrupalCon, and we don't even know for sure what it would look like technically. Whatever we'd start building now, it'd very tight, because we'd have to do a fair amount of experimenting.
We have plenty of polishing to do before then.
If we're continuing down the path @effulgentsia so eloquently paints in #25, we'll need an addition/clarification for #3462705: [META] Missing features in SDC needed for XB: we'll need SDCs to be able to express:
Right?
Back to you, @lauriii!
Comment #27
effulgentsia commented@lauriii and I discussed this and he gave me some compelling arguments for props (approach 2 in #25). I'm thinking that over and also thinking whether we can do that while still accommodating future use cases of web components and JS (React/Vue/Svelte/etc.) components.
Comment #28
wim leersMarking to signal that the next step is on @effulgentsia's plate (he told me that in DM, so I'm not just imposing that or anything 😜).
Comment #29
pdureau commentedIf my understanding is correct, those are examples of WYSIWYG / display builders configurations, not UI components definitions. And none of them are using JSON Schema notation anyway.
If we go this way anyway, can we stay compatible with JSON schema specification by not introducing drupalisms or anything related to XB in the prop definitions?
For example, this is wrong because the
enumkeyword will limit available strings to "<em>" and "<stong>" instead of acting as#allowed_tagsproperty of#markuprenderable:Comment #30
effulgentsia commentedFor JSON schema, I think we can represent it (in YAML) as:
See contentMediaType and custom annotations.
In practice I think there's some decent options available to us for passing HTML content from a rich text editor through as props/attributes to JS framework components or web components. For JS framework components, the JS framework component can internally delegate the responsibility to a component dedicated to rendering HTML content from a CMS. For example, for React there's react-html-map and probably various other options. Or, people can roll their own using whatever the framework's approach to rendering HTML strings is (for example, in Svelte, using {@html ...}) combined with an XSS filtering library like DOMPurify.
For vanilla web components (does anyone actually write vanilla web components these days now that https://lit.dev/ exists?), someone could either use
.innerHTMLcombined with an XSS filtering library like DOMPurify, or we could add an optional PHP-side rich-text prop to slot converter when rendering the web component's custom element.So from a technical standpoint, I don't have objections to rich text as a prop anymore.
The main one is being able to express order. For example, say you have an SDC with "props":
And you want
descriptionto be able to contain HTML formatting like<strong>,<em>, etc. If we modeleddescriptionas a slot, we'd need some way within the SDC's YAML to specify that within editing UIs (like XB), we want the input entry fordescriptionto be aftertitleand beforelink. Any syntax for doing that would already necessitate the SDC developer's mental model being "it's a slot but in a way it's kind of a prop", so actually making it a prop cleans that up.Comment #31
pdureau commentedCool finding! I am happy to know about
contentMediaType.For this specific example, if we consider
titlea slot (which it is IMHO), we don't need to have such elaborated solution to get description after title (first the slots in the declared order, then the props in the declared order) 😉This looks like I am nitpicking, and I like the
contentMediaTypeproposal, but my concerns are broader.We are talking here about adapting the component definition because of the display order in the XB contribution form. This is what I had in mind when I said:
I left this thread when I read about "business-related UI components", but I am an hopeless optimist.
SDC freed us by giving us the opportunity of doing front-end without caring about Drupal as a CMS, business rules and application state. Now, we can build the best UI components possible, without thinking about Views, Field API, Layout Builder or Experience builder... and then use them with all their glory in a Drupal context. Let's embrace this.
It will be a key success indicator if XB is shipped without any SDC components AND is able to leverage correctly any SDC component from the Drupal community.
Comment #32
mherchelI haven't read through the comments, but @lauriii wanted me to post my use case here for a question that I was asking:
Comment #33
wim leersComment #34
wim leersSibling issue: #3460230: Component entity config dependencies are incomplete: missing ComponentSourceInterface::calculateSettingsDependencies() to compute deps for field storage + instance settings + widget.
Comment #35
wim leersBased on #30, I think that this implementation sequence would be viable:
StaticPropSources usingtextfield type using CKEditor 5(This example matches
.)
This uses JSON Schema's
contentMediaTypeas well as JSON Schema's custom annotations usingx-…, both surfaced by @effulgentsia (👏) in #30.Note that this metadata nicely also conveys the necessary information to know how to offer in-place editing in the future (see #3453690: [META] Real-time preview: supporting back-end infrastructure):
x-format: inlinewould cause CKEditor 5's inline editor to be used, andx-format: blockwould cause CKEditor 5's balloon editor to be used.x-format: inline, we'd use axb_inlinetext format (restricted to<strong> <em> <u> <a href>) configured to use CKEditor 5 (with Bold, Italic, Underline and Link buttons).x-format: block, we'd use axb_blocktext format (restricted to<strong> <em> <u> <a href> <p> <br> <ul> <ol> <li>) configured to use CKEditor 5 (with Bold, Italic, Underline, Link, Bulleted list and Numbered List buttons).Both use only
filter_htmlfilter plugin to capture HTML restrictions. Zero other filter plugins are used. This ensures that it's not necessary to perform filtering (which can happen only on the server) when typing in the CKEditor 5 instance, hence allowing for true real-time previews! (Theoretically this imposes a security risk, but in reality this only allows self-XSS if the user manages to type a tag not listed, that CKEditor 5 then also renders. For end users, i.e. when not editing components through XB and performing this pure client-side update of the preview, the actual filtering process would still occur.)We'd decorate the
FilterFormatandEditorconfig entity types' storage handlers to hide these 4 config entities, because neither config entity type supports (un)locking.⚠️Big caveat: IIRC there are problems/limitations around CKEditor 5's ability to be configured to use only inline elements — IIRC
<p>and<br>are fundamentally assumed to be allowed.DynamicPropSources usingtextfield type using CKEditor 5StaticPropSourcecase, but given #3455629: [PP-1] [META] 7. Content Templates — aka "default layouts" — affects the tree+props data model is also moving forward, we should assume that it needs to be possible to maptextfields (aka structured content)'s HTML markup, too.To do this, we need to be able to identify which fields contain markup. We already have that:
StringSemanticsConstraint::MARKUP, introduced in April 2024, before XB even used d.o.The remaining challenge: these fields can use arbitrarily complex text formats and editors. Both bring challenges:
filterplugins, not just thefilter_htmlfilter. They virtually all require a round trip to the server:filter_image_lazy_load,filter_captionetc. all areTYPE_TRANSFORM_REVERSIBLE-type filters.For these text formats, typing in the CKEditor 5 instance will require a round trip to the server to render a preview, so "true real-time updates
will be impossible.
Note that all this is a solved problem: Drupal 8 core's Quick Edit module tackled exactly this. It's been ejected out of core, but lives on in contrib: https://drupal.org/project/quickedit. XB will be able to reuse the infrastructure it added, and will only need a subset of it.
xb_inlineandxb_blockFilterFormat+Editorconfig entities, by making them visible in the UI like all others, but provide warnings and confirmation dialogs. Add validation logic that forbids adding more filters to the text format, because that would introduce a dependency on the server, hence breaking true real-time previews.(Until this is implemented, if ever, the config can still be modified directly by developers.)
x-allowedTagsthat @effulgentsia proposed in #30.If/when that is present, we'd not use the aforementioned text formats, but instead use
x-formatsolely as a hint for how to offer in-place editing, and generate an in-memory-onlyFilterFormat+Editorconfig entity object based on those restrictions, with corresponding CKEditor 5 plugins at their default settings, and with buttons added to the toolbar if they are needed for those HTML tags (thanks to\Drupal\ckeditor5\SmartDefaultSettings::addToolbarItemsToMatchHtmlElementsInFormat().And/or alternatively: we could introduce a
x-ckeditor5whose value must be a value that passes the validation logic fortype: editor.settings.ckeditor5. That would allow complete control by the SDC developer, at the cost of very complex metadata.The point of all this: thinking through what it'd mean to go much further in allowing SDC developers to specify the authoring experience for Content Creators.
Comment #36
wim leersRemoving #3460230 as related issue, see #3460230-13: Component entity config dependencies are incomplete: missing ComponentSourceInterface::calculateSettingsDependencies() to compute deps for field storage + instance settings + widget for why.
Comment #37
wim leersMissing piece for
DynamicPropSourcein #36, also added to the issue summary:(Identified thanks to outlining roughly how the shape matching logic would need to be updated — see attached patch.)
I'd like @effulgentsia to review my proposal in the issue summary, given his strong influence on it 😊
Comment #38
wim leersAddition spotted by @lauriii: XB should match a
Drupal \Drupal\Core\Field\Plugin\Field\FieldType\StringItemfield against atype: string; x-format: inlineSDC prop.That addresses part of what #37 surfaced 👍
Comment #39
pdureau commentedHi Wim, it has been a long time you haven't heard of my warnings so here I am again 😉
The mission of SDC developers (as component authors) is to provide the best implementation for UI components, they don't have to think about the business/applicative/CMS side, so they don't have to specify the content editor experience. Once a component author starts to think about the CMS, he is in risk of lowering the quality of its UI Component.
It is not only a theoretical stance, we are doing this with UI Patterns since 2017 and it works well. UI Patterns 2.x is the proof it still work (and work even better IMHO) with SDC: we are able to inject data to slots & props from many sources.
Content editors are not the only users of a component. An UI component has to be as usable in other contexts (non editorial, unexpected constraints, data retrieval from API...).
So I am afraid this
x-format: inline/blockwill cause harm to XB in the future.Comment #40
effulgentsia commentedThe approach proposed in the issue summary looks great to me, except for 3 minor points:
I agree with #39 that we shouldn't do this for the same reasons as explained in that comment. But since it's listed as a follow-up that's even later than all the other follow-ups, we don't need to come to a consensus on it here.
May I suggest changing it to x-formatting-context? Although that's more verbose, it grounds it in a specification with a precise meaning. And if it's not specified, we should default to
block. @pdureau: Would that resolve your concern with it? If not, why not? Why do you see it as violating separation of concerns for the component author to specify that the component is designed for HTML prop foo to be contained in an inline formatting context?I think we should allow
<br>as well, since that's an inline-level element and example 1 in comment #10 seems to have one between "Developers" and "Download", unless those are intended to be two different props.Comment #41
wim leers#39: I don't disagree with you. 😊 Nor do I entirely agree with you 😄
I think ultimately you're worried about the separation of concerns: I think you're arguing for a stricter separation of concerns than what @lauriii is arguing for.
It's a spectrum, and I think from your POV, one particular point in the spectrum is correct, but it comes with other consequences (unable to automatically make reasonable matches for corresponding field type + widget aka storage + authoring UX). From @lauriii's POV, those consequences move us too far in the wrong direction, so he argues for a different point in the spectrum.
And I think the balance that @effulgentsia's proposal struck is a very reasonable one: inline vs block markup is actually something that in principle could even be derived from the HTML tag in the SDC that will contain the prop! So I second @effulgentsia's question in #40.
I think neither is wrong nor right. I think it's a matter of perspective, goals, requirements.
#40: TIL
formatting context! 😄 Updated issue summary withs/x-format/x-formatting-context/, and madeblockthe default 👍I am somewhat worried about adding
<br>to the default list of allowed tags for theinlineformatting context, because I think it could too easily result in abuse: entering multiple<br>s would easily result in visual brokenness (and clearly questionable markup, not too far removed from table-based design :P)? But that is a decision that could easily be made at a later time. Curious what @lauriii thinks.So the only remaining concern AFAICT then is allowing
<br>in thexb_inlinetext format by default or not — great, then I think we have a pretty decent plan here :DComment #42
lauriiiI don't think we need to allow
<br>for inline content to build #10. If the design is supposed to have text on two lines regardless of the width, it should not be managed with a RTE.Usually the use of
<br>results in more problems that it solves so I'd say we should probably avoid getting to those. If we don't allow<br>, explaining inline text and regular rich text remains simple since one allows multiple lines and one doesn't.Comment #43
lauriiiComment #44
wim leers#42 is exactly what I thought @lauriii would say 😄 👍
@lauriiii You and I worked closely together on bringing CKEditor 5 to Drupal core. Can you remember this:
?
Also: I'd like you to read and approve the proposed resolution in the issue summary: point 4, both of the Q2-targeting
<dt>s. You've in the past had strong opinions on the SDC DX, so the concrete proposal should get your +1.Comment #45
lauriiiProposed solution makes sense 👍
Comment #46
wim leersComment #47
salmonek commented@Wim IIRC you asked for my view on this:
Yes, by default everything must be wrapped with
<p>(or<hX>or maybe some other element).That is an implication of CKEditor 5 internal model's schema:
As per CKEditor 5 documentation:
https://ckeditor.com/docs/ckeditor5/latest/framework/tutorials/crash-cou...
As metioned schema can be updated. I tried this in a custom CKEditor 5 plugin:
$textis a any text input$rootis an root of the documentBy default
$textis allowed among others inparagraphand that one is allowed in$rootAfter the schema update I think I got the result you want to implement (source editing enabled here):

Additionally I was unable to insert a new paragraph with enter key also use some plugins (lists mostly) as they most probably aren't allowed in

$root:<br>is allowed in$rootand apparently there is no way to removeallowInentry in element definition. I'll consult that with core team.Comment #48
wim leers@salmonek Thanks for your incredibly thoughtful response!
AFAICT that means that you're saying that
x-formatting-context: inlinewon't be possible to support without allowing<br>s (which @lauriii did confirm in #42 as something we should indeed not allow). Is that right?Comment #49
wim leers@bnjmnm is already working on making CKEditor 5 load in the content entity form (e.g. for the article node type's body field) over at #3512867: CKEditor 5 not loading on formatted text Field Widgets in the content entity form.
But that issue also aims to add support for it in the component instance form — and this issue blocks that. So, tagging .
Comment #51
wim leersImplemented the plan @lauriii approved in #45. Accelerated this by the use of Claude 3.7. It failed miserably when I just asked it to implement the proposed resolution, so it's really me telling it what to do EXACTLY and then tweaking even that. The
DynamicPropSourceis implemented entirely by hand.Here's where that's at:
all-propsSDC: required vs optional, formatting contextblockvsinlinevs noneThings not yet implemented/deferred to follow-ups:
richTextclient-side transform to use the API provided by the Text Editor module (or perhaps directly CKEditor 5 initially) to retrieve the user's input without having to talk to the server (no-op implementation present already intransforms.ts): #3484395: CKEditor 5 not loading on formatted text Field Widgets in the component instance formRE: modals: that will need to be solved as a follow-up of #3484395: CKEditor 5 not loading on formatted text Field Widgets in the component instance form
RE: filter types and round trips: irrelevant for now, because we don't yet have pure client-side updates of the preview based on the content entity form yet today.
So: we need 3 follow-ups, plus the 2 follow-ups listed in the issue summary that were already out of scope here.
Comment #52
wim leers@tedbow is the only other person who knows the shape matching infrastructure, so asking him to review :)
Comment #53
tedbowThanks, reading through
Comment #54
tedbowJust fixed 1 link in the summary
Comment #55
effulgentsia commentedWouldn't it be possible to hack around this though? For example, implement a plugin that responds to events (keyUp?, maybe others?) by checking if any
<br>s made their way in, and if so, remove them?Comment #56
tedbow@Wim Leers this looks good to me. I updated test expectations to get tests to pass. The expectations look correct to me but you probably should review.
Comment #57
wim leersThanks for the very fast follow through, @tedbow! 🙏🥳 And thank you for finishing what I started — I had only run a few tests locally, and I see there was a whole range of (thankfully simple) failures.
You made my Monday much nicer 😊
On top of what you did:
Now I agree with the RTBC :)
Missing follow-ups that I identified in #51 created:
Comment #58
wim leersUpdating issue status to reflect that CKEditor 5 support is not yet present.
Comment #59
wim leersComment #61
wim leersComment #62
wim leers— #51
Full disclosure at the end:
Comment #63
wim leers