Update: We have finalized the tags and attributes for all new rich text formats and created guidelines for handling new tags in the future. Please feel free to leave questions or comments below, including how this may affect your apps. Before launch, we’ll be publishing this content on our developer docs.
New HTML Tags in Asana Rich Text
For examples in this post, we’ve added indentation and newlines to make it easier to read. When implemented, rich text routes won’t include these.
Headers: <h1></h1>
and <h2></h2>
Attributes: None
Inner content: Only <strong>
, <em>
, <u>
, <s>
, <code>
, <a>
allowed inside header tags, and no new line character allowed in the inner content.
Example: <h1>This is a header with some <strong>Strong<strong> and <em>Emphasized</em> words</h1>
Notes:
Note that we do not support h3+.
These are rendered like a heading element if possible, otherwise should be rendered as bold.
Horizontal Rule: <hr>
Attributes: None
Inner content: None
Example: <hr>
Notes:
This is an empty element, i.e. no closing tag.
These are rendered as a horizontal rule if possible, otherwise should be rendered as a newline.
Images: <img>
Attributes:
- Read:
- data-gid: attachment id
- src: s3 link to download the asset
- alt: alternate text, used for fallback / accessibility
- style: used to help render this in a webview, see notes.
- Required for write: data-gid (see note below)
Inner content: None
Example:
<img
data-gid=”12345”
src=”https://s3.amazonaws.com/assets/123/Screenshot.png”
alt=”\nhttps://s3.amazonaws.com/assets/123/Screenshot.png”
style=”display:block;max-width: 100%; margin-left: auto;
margin-right: auto;”
>
Notes:
This is also an empty element, so no closing tag supported.
This represents an image block. Even though <img>
is not a block level element , we render them as blocks in our product. Therefore we also supply a style attribute with “display: block”
. The style attribute also contains other styles to make the image display in a similar fashion as the ones in our project. We will always supply this style attribute.
Attributes required for writes: data-gid
. All other attributes are ignored on write. Note that we do not support referencing external images via the src
attribute - all image blocks have to reference images uploaded to Asana.
We currently have no way to create image blocks through the API, but do plan to provide a route to do this. We will update the forum when this is available.
Embeds: <object></object>
As part of rich text work, we’ve started adding the ability to embed rich content into text. These include both embedding views for Asana objects, like milestones, as well as external media. When exposing this in the API, we want to both support developers who want to richly display this content in their apps, as well as support a graceful fallback.
To do this, we’re using an <object>
tag. Each tag will have:
- A
type
attribute: This is MIME type. You can use this to determine the type of embed, and decide how you want to display it. This is required for writes. data
attributes: these will contain the data you need to richly display the embed.- HTML fallback in the inner contents: the content inside the object tag will be Asana HTML, representing a “less rich” version of the contents. If your app is not going to support these as rich embeds, you can render the contents of an
<object>
tag to display a less rich version of the content. This content will always be constructed and supplied by Asana. Developers will not have control over it - it will be ignored if you create or edit a<object>
tag.
<object>
tags are blocks, and we similarly supply a style attribute with “display: block”
. If clients use the inner fallback content, they must render the content as it is and not try to transform it, because we may update the fallback content in the future.
For this launch, we will introduce 3 new types of embeds: external, project milestone, and project goals.
External media embed
Attributes:
- Read:
style
: used to help render in a webview.type
: see above.data
: URL to the external media.
- Write: required
type
anddata
Example:
<object
style=”display:block”
type=”application/vnd.asana.external_media”
data=”https://www.youtube.com/embed/VqnMA3K6-e0”
>
<a href=”https://www.youtube.com/embed/VqnMA3K6-e0”>
https://www.youtube.com/embed/VqnMA3K6-e0
</a>
</object>
Notes: We recommend rendering these as either links or rich previews of the content.
Project Milestones Embed
Attributes:
- Read:
style
: used to help render in a webview.type
: see above.data-project-gid
: the id of the project we want to render milestones for.
- Write: required
type
anddata-project-gid
Example:
<object
style=”display:block”
type=”application/vnd.asana.project_milestone_list”
data-project-gid=”12345”
>
<a href=”https://app.asana.com/0/12345/list”>project name</a>
<ul>
<li><a href=”https://app.asana.com/0/12345/23456”>milestone name 1</a></li>
<li><a href=”https://app.asana.com/0/12345/23457”>milestone name 2</a></li>
</ul>
</object>
Notes: We recommend rendering this as a list of milestones in the project.
Project Goals Embed
Attributes:
- Read:
style
: used to help render in a webview.type
: see above.data-project-gid
: the id of the project we want to render goals for.
- Write: required
type
anddata-project-gid
Example:
<object
style=”display:block”
type=”application/vnd.asana.project_goal_list”
data-project-gid=”12345”
>
<a href=”https://app.asana.com/0/12345/list”>project name</a>
<ul>
<li><a href=”https://app.asana.com/0/goal/23456”>goal name</a></li>
<li><a href=”https://app.asana.com/0/goal/23457”>goal name</a></li>
</ul>
</object>
Notes: We recommend rendering this as a list of goals associated with the project.
Tables: <table></table>
, <tr></tr>
and <td></td>
Tags:
<table>
for the table,<tr>
for each table row<td>
for each table cell.
Attributes:
- No attributes on
<table>
or<tr>
<td>
can have these optional attributes:colspan
,rowspan
- these denote how much the cell spans in the row and column direction, the same as html. Note that if you provide acolspan
attribute, you must also providedata-cell-widths
(see below)data-cell-widths
- this denotes the width of the cell in pixels. It is a list of comma separated numbers. The length of the list is the same as colspan.
- Attributes that will be ignored for writing:
width
: this will be the sum of widths indata-cell-widths
. We supply this to make sure that the table cell renders as expected. To set the width, however, usedata-cell-widths
Inner content:
- Only
<tr>
allowed in<table>
- Only
<td>
allowed in<tr>
- Only
<strong>
,<em>
,<u>
,<s>
,<code>
,<a>
,<img>
,<ul>
,<ol>
,<li>
allowed in<td>
. New line characters also allowed in<td>
Example:
<table>
<tr>
<td data-colwidth="320" width="320">
Heading 1
</td>
<td data-colwidth="180" width="180">
Heading 2
</td>
<td data-colwidth="120" width="120">
Heading 3
</td>
</tr>
<tr>
<td data-colwidth="320" width="320">
text
<img
data-gid=”12345”
src=”https://s3.amazonaws.com/assets/123/Screenshot.png”
alt=”\nhttps://s3.amazonaws.com/assets/123/Screenshot.png”
style=”display:block;max-width: 100%; margin-left: auto;
margin-right: auto;”
>
</td>
<td colspan="2" data-colwidth="180,120" width="300">
text
<ol>
<li>List iem</li>
<li>List item</li>
</ol>
</td>
</tr>
</table>
Guidelines for handling new tags in the future
We plan to add new rich text elements going forward, and we want you to be able to design parsers and display logic for our rich text in a forward compatible way. To do this, we’re providing two mechanisms for you to be able to parse and display new tags that you don’t explicitly support: giving sane defaults to render it in a webview, and guidelines for how to handle new tags.
Forwards compatibility option one: Render tags in a webview
You can expect the rich text HTML to render reasonably in a web view if you apply the following css style to the wrapping DOM node:
overflow-wrap: break-word;
white-space: pre-wrap;
Note that this won’t look like it does in Asana necessarily, and you may find that you want to add your own styles.
Forwards compatibility option two: graceful fallbacks
If you aren’t using a webview, in order to avoid missing or mangled text, here is how you should render tags you don’t explicitly handle:
<object>
with an unhandled type
Render the <object>
tag as a block and render the contained HTML with the same behavior as if it weren’t inside an <object>
. We will never send an <object>
tag nested inside another <object>
tag.
<img>
Fall back to the alt text if the image can’t be displayed. Render \n<alt text>\n
since <img>
are blocks
Empty elements except <img>
It is ok to omit them. Render as a new line if the tag is a block.
Other semantic non-terminal tags
Ignore the tag and render whatever is inside. Follow the html convention for whether it is a block or not.
Other rich text guidelines
How to determine if a tag is block or inline
- If the tag and its attributes have API documentation, follow the documentation (e.g. milestone list embeds are blocks), otherwise…
- If the tag is
<object>
or<img>
, render it as a block - Check if it falls under the html block level element list
When processing rich text and sending it back
It’s ok to ignore tags or attributes on tags that are unknown for rendering/processing. But it’s very important to send everything back (attributes and inner content) to avoid data loss. <object>
is an exception where it’s ok to not send any inner content back (all inner content in <object>
will be ignored).
If you plan to write an editor
For editing, even if the tag and attributes are known, if it contains any unknown attributes, it must be treated as unknown.
If a tag is unknown, first determine if the tag is block or inline and render it as a block or inline atomic and non-copiable (and non-cut&paste-able) editor node (all inner content is non-editable). This is because we don’t know if the unknown node has constraints on inner content or where it can appear. The node must also keep track of all attributes and inner content to be serialized back.