Replies: 21 comments 81 replies
-
cc: @mtias @gziolo @draganescu |
Beta Was this translation helpful? Give feedback.
-
Thanks for summarizing your thoughts here. The first thing I'd like to elucidate is whether the abstract concept makes sense for all these use cases that are outlined. For example, I'm not sure the featured image case has any resemblance to a "display current year" token beyond its dynamic nature. There could be more straightforward paths if we could just mark an attribute as being dynamic under certain conditions:
I think these are different in that they don't deal with explicit markup beyond what the block has already defined, and should be thus trivial to resolve from a block perspective because once "edit" consumes the attribute it doesn't matter where the url for an image came from. It doesn't need to provide any further fallbacks outside of setting the media attribute to a specific value if necessary. For proper inline shortcodes / tokens, I am not sure we should support arbitrary fallbacks outside of plain text within the |
Beta Was this translation helpful? Give feedback.
-
I'm definitely hitting some initial obstacles to this method. Specifically I'm having trouble figuring out how to associate the token during parse with the attributes in a block. Deferring the parse of the tokens until later on in block processing (and in PHP render functions) means we have lost some association with the attribute selector. E.g. we say that a token applies to |
Beta Was this translation helpful? Give feedback.
-
@draganescu landed #39658 that makes the Cover block dynamic by adding the option for featured image binding. It’s a good example of the block that would benefit from the standardized API discussed here. @justintadlock shared some interesting ideas on the similar topic in the following article Featured Cover Blocks and the Future of Binding Data to Generic WordPress Blocks on WP Tavern. Here are some highlights:
|
Beta Was this translation helpful? Give feedback.
-
Something notable about blocks is that they can hypothetically be serialized into a variety of formats. It's easy to represent the block tree as JSON. The data is clearly structured, and is not necessarily tied to HTML as an output format. Can (and should) this apply to tokens? What's the abstract representation of a token outside of HTML? How could a token be associated with a particular HTML attribute without becoming dependent on the HTML markup? Is that a bad thing? Is this system conceptually related to blocks, or should it be considered a separate thing entirely? Or reworded: is this an extension of the block API, or is it just a more fallback-friendly version of shortcodes? |
Beta Was this translation helpful? Give feedback.
-
Just wanted to share my observation based on the discussion here and when talking about similar use cases in the past. I see a few different scenarios that we try to address with a single proposal. I was able to identify 4 different types:
Can you identify other scenarios? Does the original proposal try to cover all of them or only a subset? |
Beta Was this translation helpful? Give feedback.
-
I've been exploring another approach which is best described as "a weird Unicode trick" but has some level of promise, even though it comes with a heavy tradeoff: encode dynamic token data as inline in-band meta information via Unicode language tags [¶23.9, BCP 47] (or some other viable invisible Unicode characters). Summary of this approach:
Granted that the editor can parse and replace these tokens the drawbacks shouldn't be insurmountable, but it means that links/URLs inside posts might be broken if rendered outside of WordPress. On the other hand I think any in-band solution will be broken at some level outside of WordPress/the editor/a conforming implementation. The language tokens (U+E0001, U+E0020 - U+E007F) mirror 7-bit ASCII (less the control characters) and give us a unique opportunity to JSON-encode attributes inside the token, giving us a full ability to specify token type as well as token variations, such as formatting parameters for a date token. The JSON encoding would need to represent non-ASCII characters in their import { token } from '@wordpress/tokens';
const tokenData = {
name: 'dmsnell/page-views',
attrs: {
style: 'approx' // vs. 'exact'
}
}
const s = `This page has been viewed ${ token( tokenData, 'many' ) } times.`;
// This page has been viewed {WP-X-TOKEN-DMSNELL-PAGE-VIEWS {\"attrs\":{\"style\":\"approx\"}}}many{/WP-X-TOKEN-DMSNELL-PAGE-VIEWS} times.
s === "This page has been viewed many times."
s.split(/[\u{E0001}\u{E0020}-\u{E007F}]+/ug) === [
'This page has been viewed ',
'many',
' times.'
] This comment describes the technical approach for storing, finding, and loading dynamic tokens and assumes separate UI work to make it convenient to enter and edit tokens. That approach would probably amount to dynamically replacing the tokens inside the editor with some single-character placemark styled via CSS to add the token's title and background (the "pill" form). |
Beta Was this translation helpful? Give feedback.
-
While I'm adding alternative approaches it struck me that we could try and directly resuscitate shortcodes with a new syntax, albeit this would involve more care on the parsing side to ensure we don't replace text showing a token that itself is not a token. By re-introducing a plaintext token syntax we solve one part of the problem of making it convenient to enter tokens, though we leave unsolved the issue that requiring people to enter complicated syntax is inconvenient. We still need a UI solution to enter and edit tokens. A plaintext syntax is similar to the idea of using hidden Unicode characters but makes the following (and probably more) tradeoffs:
|
Beta Was this translation helpful? Give feedback.
-
IntroI've been thinking about this and I struggle to land on which generic problem are we trying to solve here. Considering that Dynamic attributes only need "standardisation"
What if simply introduce a type "function" to any attribute. We currently support:
We could add a "function" type, or something, and call that to return some thing or do whatever. This can be solved through any of the current filters, but this would be a generally available solution, a standard way of doing it. The "function" type would support a "default" prop for fallback if the "callback" prop function does not execute, does not exist or whatever. In the featured image example the "default" could be a placeholder image. I think, today, for "dynamic" attributes, whatever we do, it's only going to be a standardisation of something we can achieve anyway through what the system already provides. Dynamic inline tokens need some APIOn the other hand, dynamic inline tokens are something we don't have a mechanism for. As a note, out of all the solutions here the invisible chars one I am least a fan of 😁 . UX wise, IMO dynamic inline tokens should be similar in interaction form to a format. The same way you select some text and make it bold, you select some text and make it "dynamic":
But what if these "dynamic token formats" proliferate like I think that maybe precisely considering them as limited as formats would block the proliferation, particularly since I don't think enabling shortcode like randomness is a good thing for blocks. In this view of inline dynamic token as a format, a solution could be added by simply offering a system wide "dynamic" format, which can be extended with any callback. The format would use the data HTML tag and the value of the
This we we reuse a thing that exists, extend it to support something very similar on a conceptual level, and get for free the UX of the feature. TLDR; at the end:
|
Beta Was this translation helpful? Give feedback.
-
One idea that sounds a little crazy but might actually work is to just use regular blocks for most "inline token" style stuff, and rework all the textual blocks to accept inner blocks. There are definitely a number of technical and UX questions to consider with this approach, but I think it's worth fully considering. I can't think of any insurmountable issues with the approach, and conceptually, it's the most simple, since it just reuses the existing block concepts (and thus inherits all of the existing APIs for free). https://github.com/stokesman/lineup/ It's worth noting that this would not cover use-cases like dynamic HTML attribute values, so those situations would still require one of the other solutions mentioned in this discussion. |
Beta Was this translation helpful? Give feedback.
-
This is a super interesting! Sorry for the long comment, but the topic touches so many things. Don't want to risk missing something. Implementation ideas:
The references in the block props that hold data for the tokens can probably be plain objects (maps):
When the block is parsed the tokens will be replaced with the output of a callback function. Simple This seems simple enough and also powerful enough to meet any needs for adding dynamic content to static blocks. The main idea is that all of the above is part of a predetermined API, i.e. no random token names, no direct user input, etc. |
Beta Was this translation helpful? Give feedback.
-
This is interesting and maybe more relevant than ever since the feature of inline shortcodes is unclear: https://core.trac.wordpress.org/ticket/58333 Where does it stand? do you think it will ever be picked up? I believe #42485, in its current state at least, isn't a solution. |
Beta Was this translation helpful? Give feedback.
-
Thanks @dmsnell . |
Beta Was this translation helpful? Give feedback.
-
Hey folks 👋 Liam from the Advanced Custom Fields (ACF) dev team here. I wanted to kickstart our input into this conversation, and see where our aspirations are overlapping and where we can contribute directly to Gutenberg rather than doing all the work inside ACF exclusively. Getting (simple) meta fields as inline tokens (or Bits, if that's the term we're going with!) with live previews is our end goal. The live preview is specifically the part I think we're going to need to add stuff to Gutenberg in order to enable. I've looked at the autocompleter system which currently only supports usernames, and wanted to achieve something similar to that, maybe with $ as the prefix to trigger, then likely a data store, combined with AJAX lookups for fields not present on the UI to select. @tkapler's mockup over on the main Custom Fields/Core Blocks ticket is a similar UI we'd like to achieve: #51373 (comment) - although, in our minds we would present this as the live data just with an outline and tip to indicate it's a live replacement. I really like @dmsnell's example implementation of a token from #42015 - that seems to be logical - and seems like it's more viable now the HTML API is more fully featured? Specifically, that token system could allow us (and other third party devs) to add additional attributes via hooks from additional components in the sidebar when the token is selected in line, for example supporting things like "Format" inputs for those who want to transform a date/time into a specific format - replicating functionality that's currently provided by legacy jquery-datetime pickers. The core of this issue is currently spread around a few different issues and discussions - and arguably my input is better suited to this one, but I think we need a bigger picture solution as I'm not sure any specific part of this system could work without the others being in place too. |
Beta Was this translation helpful? Give feedback.
-
Since I've long promised updates here I'm finally delivering some news that I think is exciting. The HTML API has changed everything about Bits. It now provides us with a way to know the context in which a Bit is found, whether inside an HTML attribute, whether in markup, whether inside a It also provides us a novel way to allow some but not all markup as a substitute for a Bit. For example, we can allow We can also process the output from a Bit's rendering function and prevent that output from bleeding into the page or changing the container in which the Bit is found. For example, if a plugin attempts to replace a Bit with The new proposed syntax relies on what I call "funky comments" which are invalid tag closers. HTML defines that if you find what looks like a tag opener but the tag name is invalid, then it should treat that as plaintext. For example, This funky comment provides the opportunity to build a flexible placeholder or token that is handled in a deterministic and universal way in HTML-compliant browsers, cannot be nested inside of itself, and cannot otherwise break its surrounding HTML any more than a normal typo would. It can't break HTML structure. It's fundamentally benign because it disappears from the rendered page if it isn't replaced.
Inside HTML markup, a Bit is an existing syntax element. Inside an HTML attribute it's plaintext. Inside an attribute the The WordPress HTML API can handle all appropriate escaping of HTML values on substitution automatically. Render functions for Bits thus return an HTML template instead of a string that will be inserted into the page. A template takes one of several forms:
Here's what that looks like: function render_my_bit( $bit_type, $attributes, $content_type, $context_attributes ) {
if ( 'attribute' === $content_type ) {
// The HTML API knows this is found inside an attribute, so the result
// will be `plaintext no matter <strong>what</strong>`
return 'plaintext no matter <strong>what</strong>';
}
if ( 'my/static-bit' === $bit_type ) {
// Since this occurs within markup between tags, the result will be
// the literal value `rich text inside <strong>markup</strong>` and
// the browser will render it boldly, or as per the stylesheet
return 'rich text inside <strong>markup</strong>';
}
return array(
'<time datetime="</%iso_time>"></%human_time></time>',
array(
'iso_time' => strftime( ISO_FORMAT_STRING, $then ),
'human_time' => __( '%s ago', human_time_diff( $then, current_time( 'U' ) ) ),
)
);
} Note that the template syntax also relies on "funky comments" to define named placeholders corresponding to the values in the supplied array. The HTML API knows how to properly escape these based on where they are found, so it's possible to reuse a value in different contexts. function render_link_bit( $bit_type, $attributes, $content_type, $context_attributes ) {
return array(
'<a href="</%url>"></%url></a>',
array( 'url' => '/search?q=<🔎>' ),
);
} This will produce the following HTML. <a href="/search?q=%3C%F0%9F%94%8E%3E">/search?q=<🔎></a> |
Beta Was this translation helpful? Give feedback.
-
With block bindings shipping in 6.5, I'd love to try and get some momentum on carrying on with the bits proposal here. It feels like block bindings are going to highlight how hard it is to output those same sources in the middle of a core block like a paragraph, so this would be a great opportunity to push this forward I think! |
Beta Was this translation helpful? Give feedback.
-
I ran into Bits while working on the new product page blocks in WooCommerce. We’ll need them to surface dynamic content, like the name of the currently viewed product in the add-to-cart notice or the out-of-stock message. I thought I could help Bits take shape and after learning more about the feature, I created a prototype using the existing link control UI. Note: the video has audio. I talk through some of the design choices and explain the rationale. bitsux_i1.movWhy link control? Firstly, it makes logical sense because Bits are like enhanced links. Additionally, a text string cannot contain both a link and a Bit, so consolidating the UIs reduces confusion. Here’s a quick rundown of the UI and UX:
Future explorationsAs a future improvement, we'd explore adding a keyboard shortcut to let users quickly insert Bits similarly to block. We'd use // or [ as a way to quickly open the Bit list. It'd work just like the in-line block inserter, meaning that users could continue typing to search variables and custom fields. We'd also explore displaying the actual Bit data in the content instead of just the popover. Bits are a fairly new concept and it isn’t entirely clear how they connect to other features, like Block Bindings. They’re one and the same, though I don’t think we can develop a coherent UI that will work well for both. I look forward to hearing your thoughts! What in the prototype above works and what doesn’t? What did I miss? |
Beta Was this translation helpful? Give feedback.
-
There is now a formal proposal from @dmsnell posted on Make Core: Proposal: Bits as dynamic tokens. |
Beta Was this translation helpful? Give feedback.
-
@jarekmorawski your proposal is both extremely thorough and amazing! This would fulfill a dire need that we've had for years now. My main concern is the ease of creating custom Resources (or whatever they're eventually called) in a theme or plugin. It would be critical that (at least most) custom Resources be creatable with only PHP and something like a Custom block development has required WP developers to delve into React and huge NPM libraries + a build process to do things the right way (outside of plugins like ACF that provide a PHP-only mechanism for scaffolding custom blocks), even to make relatively simple blocks. This has been frustrating for us in some cases because of how difficult it might make maintaining one-off blocks (returning to them potentially years later and trying to use an old build process with outdated NPM packages etc). Custom Resources would very often be one-off additions to a child theme for an individual site, left untouched for potentially many years before needing any tweaks. Having some kind of build process with a bevy of dependencies will make that a nightmare. Long story medium: developers will need a way to rapidly build durable custom Resources that don't require a bunch of tooling. It needs to be as simple as creating a Shortcode. |
Beta Was this translation helpful? Give feedback.
-
Thanks for all the comments so far. Much appreciated! I updated the flows to consider the existing Block Bindings UI and other applications for bits, like metafields. I also looked at the Connections panel to see if we can connect the dots between the proposed UX for Bits and the existing flows. Curious to hear your thoughts! New inline Bits UXbits_inline.movBits in metafields like Alternative Textbits_fields.mp4Updates to the Connections UI and how it connects to Bitsbindings_panel.mp4 |
Beta Was this translation helpful? Give feedback.
-
Hey @dmsnell 👋 Would you be able to post an update here about where we are at / how one can help move this forward? :) What are the next steps here in order to make progress? |
Beta Was this translation helpful? Give feedback.
-
Status
See the proposal on Core Make
Summary
Blocks established a unified and elegant interface for arranging and composing high-level web content, specifically "block" content. One area the block model has been deficient, however, is in establishing more granular semantic types of "inline" content that carry meaning not associated with a block. These inline semantic parts represent content or metadata that comes from outside the document being edited and may be relatively static or highly dynamic.
These small semantic things will be referred to as Bits.
Examples:
"tokens" are now "bits"
This proposal previously discussed the terms "dynamic tokens" and "inline tokens" and more briefly, "tokens." In all cases the new terminology will follow the term "Bits" unless explicitly distinguished therefrom.If we could introduce blocks everywhere we might not need Bits, but we cannot, for instance, include a block inside an HTML attribute or even inside a
RichText
field. A Bit is a kind of indicator or marking inside a document for a region that will be replaced based on some external data. They can appear in almost any place within a document, within a block, within HTML.Overview of this proposal:
<p>
tag, but they cannot determine which HTML tag name to print. (There are examples with shortcodes doing this, e.g.<[my_heading_tag]>…</[my_heading_tag]>
).RichText
area a Bit may be outlined by a border and contain the name of the type of Bit it is and a preview of what its replacement content might look like.plugins_url()
in PHP in order to set an image block's source as an asset in a plugin's contents.register_bit()
function or one similarly named. The input arguments will be: the Bit name and attributes, whether the Bit is found inside an HTML attribute or in markup, and the context attributes provided by surrounding blocks. The output shall be an HTML template that WordPress will render into the place the Bit is found.<Bit>
editor element provides block authors a way to insert Bits around content in their blocks.RichText
for inline token content.Mockups
Usage
Indicate that an image should use the featured image as its source.
Insert day of week of post publishing time
Consider we have written "When I published this post on [Day of week of publish time], I was hungry." The snippet inside
[]
represents where we have inserted a Bit throughRichText
.Related discussions
src
Read More
block (post link) #37649: Create dynamic attributes for things like custom "Read more" linksBeta Was this translation helpful? Give feedback.
All reactions