# Text Component — LLM Reference `Text` is the **primary typography primitive** in this email builder. It renders styled text content inside a table/TD structure for email client compatibility. It accepts an HTML string via `config.text` (with link styles injected automatically) and supports the full range of CSS text properties, padding, background color, and an Outlook-safe `maxWidth` constraint. --- ## Position in the Document Tree ``` DocumentTree └── Section └── Container └── Column / Row └── Text ← leaf component, renders styled text ``` `Text` is a leaf node — it does not have structural children in the component tree. It sits at the end of the layout hierarchy inside a `Column`, `Row`, or directly inside a `Container` cell. --- ## ComponentNode JSON Structure ```json { "id": "unique-string-id", "type": "TextComponent", "config": { "text": "
Hello world
", "padding": "16px 24px", "color": "#111827", "textAlign": "left", "fontFamily": "Arial, sans-serif", "fontSize": "16px", "fontWeight": "400", "fontStyle": "normal", "lineHeight": "1.6", "letterSpacing": "0px", "textTransform": "none", "textDecoration": "none", "backgroundColor": "#ffffff", "opacity": 1, "whiteSpace": "normal", "wordBreak": "break-all", "maxWidth": "480px", "direction": "ltr" } } ``` --- ## `TextConfig` — Full Property Reference ### Content | Property | Type | Description | |---|---|---| | `text` | `string` | HTML string rendered via `dangerouslySetInnerHTML`. Link styles are automatically injected to match the text's color and decoration. | ### Layout | Property | Type | Example | Description | |---|---|---|---| | `padding` | `string` | `"16px 24px"` | Applied to the containing `` — paragraph - `
` — quoted text ### Permitted inline tag: `` only The **only permitted inline tag** is ``. It may carry a `style` attribute, but only with the CSS properties listed in the allowed inline styles below. **All semantic inline HTML tags are forbidden.** This includes, but is not limited to: | Forbidden tag | Reason | |---|---| | `` | Use `` instead | | `` | Use `` instead | | `` | Use `` instead | | `` | Use `` instead | | `` | Use `` instead | | ``, ``, `` | Use `` instead | | `` | Use `` instead | | `` | Use `` instead | | ``, `` | Not supported | | `
` | Use separate `` elements for line breaks | | `
` | Images are not permitted inside `text`; use a dedicated image component | | Any `
`, ``, structural tags | Not permitted inside `text` | ### Permitted inline tag: `` for links The `` tag is permitted for hyperlinks. See **Link Style Injection** below for detailed behavior. ### Allowed inline styles on `` and `` When using `` or ``, only the following CSS properties are permitted. These correspond to the properties the parent `TextConfig` already manages — using them inline overrides those values for the spanned content only. | CSS property | Example value | |---|---| | `font-weight` | `"700"`, `"400"`, `"bold"` | | `font-style` | `"italic"`, `"normal"` | | `font-size` | `"14px"`, `"1.2em"` | | `color` | `"#6366f1"` | | `background-color` | `"#fef9c3"` | | `text-decoration` | `"underline"`, `"line-through"`, `"none"` | | `letter-spacing` | `"1px"` | | `text-transform` | `"uppercase"`, `"capitalize"` | | `vertical-align` | `"super"`, `"sub"` | | `font-family` | `"Georgia, serif"` | **Do not use** layout-related properties (`margin`, `padding`, `display`, `width`, `float`, etc.) on inline elements inside `text`. These are not meaningful in this context and will produce unpredictable results in email clients. ### Correct and incorrect examples **✅ Correct — use `` with `font-weight` for bold text:** ```json "text": " Your order is confirmed.
" ``` **❌ Incorrect — do not use ``:** ```json "text": "Your order is confirmed.
" ``` **✅ Correct — use `` with `font-style` for italic text:** ```json "text": "Terms apply. See terms and conditions.
" ``` **❌ Incorrect — do not use `` or ``:** ```json "text": "Terms apply. See terms and conditions.
" ``` **✅ Correct — use `` with `color` for inline color changes:** ```json "text": "Price: $0.00
" ``` **✅ Correct — use multiple `` tags instead of `
`:** ```json "text": "Line one
Line two
" ``` **❌ Incorrect — do not use `
`:** ```json "text": "Line one
" ``` **✅ Correct — link with manual color override:** ```json "text": "
Line twoVisit our site
" ``` --- --- ## Liquid Template Language When the context supports it (for example, building a Shopify email template), Liquid template syntax can be used inside `config.text` to inject dynamic values at send time. **Liquid is only supported when the component has no data bindings.** Do not combine Liquid syntax with `bindings.propertyMap` on the same property — the two systems are mutually exclusive. ### Supported Liquid Syntax | Syntax | Description | Example | |--------|-------------|---------| | `{{ variable }}` | Output a variable | `{{ customer.first_name }}` | | `{{ variable \| filter }}` | Output with a filter | `{{ order.total_price \| money }}` | | `{% if %}...{% endif %}` | Conditional block | `{% if customer.first_name %}Hi {{ customer.first_name }}{% endif %}` | ### Examples **Simple variable output:** ```json { "id": "greeting-text", "type": "TextComponent", "config": { "text": "Hi {{ customer.first_name }}, thanks for your order!
", "fontSize": "16px", "color": "#111827", "lineHeight": "1.6" } } ``` **Variable with filter and inline styling:** ```json { "id": "order-total", "type": "TextComponent", "config": { "text": "Order total: {{ order.total_price | money }}
", "fontSize": "16px", "color": "#374151", "lineHeight": "1.5" } } ``` **Conditional content:** ```json { "id": "conditional-text", "type": "TextComponent", "config": { "text": "{% if customer.first_name %}Welcome back, {{ customer.first_name }}!{% else %}Welcome to our store!{% endif %}
", "fontSize": "16px", "color": "#111827", "textAlign": "center" } } ``` ### Rules & Constraints - **No data bindings when using Liquid.** If `bindings.propertyMap` maps `config.text` to a Data Layer path, do not also include Liquid tags in `config.text` — use one or the other. - **Liquid tags are passed through as-is** in the `text` string. They are not processed or validated by the component — they are rendered by the platform (e.g. Shopify) at send time. - **Liquid can be mixed with permitted HTML and inline styles.** Wrap Liquid output in `` tags to apply styling to dynamic values. - **Liquid is context-dependent.** Only use it when the email platform is known to support Liquid rendering (e.g. Shopify). Do not include Liquid syntax in generic email templates. --- ## `maxWidth` Behavior `maxWidth` uses the same Outlook Classic compatibility pattern as `Column.maxWidth`: - The outer `` stays at `width: 100%` — it always fills its parent cell - Inside it, ` ` handles horizontal centering in Outlook Classic (Word engine) - Inside ` `, an inner table with `width={maxWidth}` as an HTML attribute — Outlook Classic respects this as a hard pixel cap - Modern clients receive `max-width: {maxWidth}` as CSS on the same table > **Important:** `maxWidth` is **not** applied to the inner ` ` — CSS `max-width` on a `` is ignored by Outlook Classic. The constraint is enforced at the table level only. **When to use:** When a Text block sits in a wide column but you want to limit the line length for readability. For example, a 600px Section containing a single text column where lines should cap at 480px. ```json "config": { "maxWidth": "480px", "textAlign": "center" } ``` --- ## Link Style Injection (`injectLinkStyles`) The `Text` component uses `injectLinkStyles` to automatically manage link styles. Understanding how it works is critical for correct AI-generated content. ### What `injectLinkStyles` Does When `config.text` is a string, the component runs the HTML through `injectLinkStyles`, which: 1. Parses the HTML and finds all `` tags 2. For each `` tag, examines its existing inline `style` attributes 3. **Conditionally injects** missing styles for `color` and `text-decoration` only when: - The property is **not already defined** on the `` tag's inline style, AND - The property is **not inheriting** a valid value from parent elements ### The Algorithm (Simplified) For each `` tag, `injectLinkStyles` checks two properties: `color` and `text-decoration`. **For each property:** - If the `` tag already has this property defined in its inline `style` attribute → **DO NOTHING** (keep the manual value) - If the `` tag does NOT have this property defined: - Try to inherit from parent elements (like a surrounding ``) - If no inherited value exists, use the `fallback` from the text component's config (e.g., `color`, `textDecoration`) - If no fallback exists for `text-decoration`, default to `"none"` **Key takeaway:** Manual inline styles on `` tags are **never overwritten**. They always take precedence. ### Link Style Resolution Examples **Example 1: No manual styles on ``** ```html Click here ``` Result: Receives `color` and `text-decoration` from text config (fallback). If config has `color: "#3b82f6"` and `textDecoration: "underline"`, the link gets those. **Example 2: Manual `color` only** ```html Click here ``` Result: - `color: #ff0000` → **preserved** (manual wins) - `text-decoration` → injected from text config (or defaults to `"none"`) **Example 3: Manual `text-decoration` only** ```html Click here ``` Result: - `text-decoration: none` → **preserved** (manual wins) - `color` → injected from text config **Example 4: Both properties manually defined** ```html Click here ``` Result: Both properties preserved. Nothing is injected. Your manual styles win completely. **Example 5: Inherited from parent ``** ```html Click here ``` Result: - No manual styles on `` - `color` is inherited from the parent `` → `#00ff00` is used - `text-decoration` injected from text config (or defaults to `"none"`) ### Summary: What `injectLinkStyles` Does NOT Do - ❌ It does **NOT** remove or override any existing inline styles on `` tags - ❌ It does **NOT** prevent you from adding any supported style property to `` tags - ❌ It does **NOT** restrict what colors, decorations, or weights you can apply to links - ❌ It does **NOT** inject styles for properties already defined on the `` tag ### Best Practices for AI-Generated Content **✅ You can safely add any supported inline style to `` tags** — they will never be stripped or overridden: ```html Custom styled link ``` **✅ You can rely on automatic injection** — if you don't specify styles, the component ensures links look consistent with your text config: ```html Link that inherits text styles ``` **✅ You can mix both approaches** — let the component handle some properties while manually controlling others: ```html Bold link that inherits color from text config ``` --- ## `wordBreak` Default Unlike all other properties, `wordBreak` has a **non-`undefined` default**: it defaults to `"break-all"` when not specified. This prevents long URLs or unbroken strings from overflowing narrow email columns in most clients. Set `wordBreak: "normal"` explicitly if you need standard word-break behavior (e.g., for CJK text or controlled layouts where overflow is handled elsewhere). --- ## React Usage ```tsx import Text, { TextConfig } from './Text'; const config: TextConfig = { text: 'Hello world
', fontSize: '16px', color: '#111827', lineHeight: '1.6', padding: '16px 24px', };``` The component is memoized via `arePropsEqual`. Pass stable `config` objects to avoid re-renders. --- ## Common Patterns & Examples ### 1. Body paragraph ```json { "id": "body-text", "type": "TextComponent", "config": { "text": " Your order has been confirmed and will ship within 2 business days.
", "fontSize": "16px", "fontFamily": "Arial, sans-serif", "color": "#374151", "lineHeight": "1.6", "padding": "0 0 16px 0" } } ``` ### 2. Section heading within Text component ```json { "id": "text-section-heading", "type": "TextComponent", "config": { "text": "Welcome back, Alex
", "fontSize": "28px", "fontWeight": "700", "color": "#111827", "lineHeight": "1.2", "textAlign": "center", "padding": "0 0 8px 0" } } ``` ### 3. Link with automatic style injection (no manual styles) ```json { "id": "footer-legal", "type": "TextComponent", "config": { "text": "You received this email because you signed up at example.com. Unsubscribe
", "fontSize": "12px", "color": "#9ca3af", "textAlign": "center", "lineHeight": "1.5", "padding": "16px 24px" } } ``` The unsubscribe link automatically receives `color: #9ca3af` via `injectLinkStyles`. ### 4. Link with manual style override ```json { "id": "promo-link", "type": "TextComponent", "config": { "text": "Special offer ends soon
", "fontSize": "16px", "color": "#3b82f6", "textDecoration": "none" } } ``` The link keeps `color: #ff0000` and `font-weight: 800` (manual wins). `text-decoration` is NOT injected because the text config has `textDecoration: "none"` which becomes the fallback. ### 5. Link inheriting from parent span ```json { "id": "inherited-link", "type": "TextComponent", "config": { "text": "Check this deal
", "fontSize": "16px", "color": "#3b82f6", "textDecoration": "underline" } } ``` The link inherits `color: #00aa00` from the parent ``. `text-decoration: underline` is injected from the text config since the link has no manual decoration. ### 6. Mixed inline styles (bold + colored span) ```json { "id": "mixed-inline", "type": "TextComponent", "config": { "text": "Save 30% on your next order.
", "fontSize": "16px", "color": "#111827", "lineHeight": "1.5" } } ``` ### 7. Constrained reading-width text ```json { "id": "article-body", "type": "TextComponent", "config": { "text": "Long-form article content goes here...
", "maxWidth": "480px", "textAlign": "left", "fontSize": "16px", "color": "#374151", "lineHeight": "1.7", "padding": "0" } } ``` ### 8. RTL text block ```json { "id": "rtl-text", "type": "TextComponent", "config": { "text": "مرحباً بك في متجرنا
", "direction": "rtl", "textAlign": "right", "fontFamily": "Arial, sans-serif", "fontSize": "16px", "color": "#111827", "lineHeight": "1.8" } } ``` ### 9. Highlighted callout text with background ```json { "id": "highlight-text", "type": "TextComponent", "config": { "text": "Limited time offer: Free shipping on all orders over $50.
", "fontSize": "15px", "color": "#713f12", "backgroundColor": "#fef9c3", "padding": "12px 16px", "lineHeight": "1.5", "wordBreak": "break-word" } } ``` --- ## Data Bindings `Text` supports the `bindings` property on the `ComponentNode`, alongside `id`, `type`, and `config`. Leaf components do not support `dataList` or `itemAlias` — use those on a parent `Container`, `Column`, or `Row` to repeat the component. `visible` and `propertyMap` are fully supported. ```json { "id": "promo-text", "type": "TextComponent", "bindings": { "visible": "user.isPremium === true", "propertyMap": { "config.text": "promo.body", "config.color": "promo.textColor" } }, "config": { "text": "Fallback text
", "fontSize": "16px", "color": "#111827" } } ``` | Property | Description | |---|---| | `visible` | JS expression evaluated against the Data Layer. Hides this component when falsy. | | `propertyMap` | Maps component property paths (prefixed with `config.`) to Data Layer paths. | > Bindable properties include `config.text`, `config.color`, `config.backgroundColor`, `config.fontSize`, `config.fontFamily`, `config.padding`, and any other scalar `TextConfig` field. --- ## Rules & Constraints - **`config.text` must only contain permitted HTML.** Block elements (``, `
`–`
`, `
`, `
`, `
- `, `
`) and `` and `` are the only allowed tags. All semantic inline tags (``, ``, ``, ``, ``, `
`, etc.) are forbidden. - **Use `` for bold, never `` or ``.** Semantic weight tags are not supported. - **Use `` for italic, never `` or ``.** Semantic emphasis tags are not supported. - **Manual inline styles on `` tags are never removed or overridden** — `injectLinkStyles` only adds missing `color` and `text-decoration` properties. - **You can add any supported style property to `` tags** (`color`, `text-decoration`, `font-weight`, `font-style`). - **`` and `` may only carry allowed inline CSS properties.** Layout properties (`margin`, `padding`, `display`, `width`, etc.) are not permitted. - **`wordBreak` defaults to `"break-all"`** unless explicitly set. This is the only property with a non-`undefined` default. Set `"normal"` to opt out. - **`maxWidth` is enforced at the table level, not on the ``.** Do not attempt to replicate this behavior using CSS on inner elements — Outlook Classic will ignore it. - **`padding` is the only spacing mechanism.** There is no `margin` on `Text`. Use `padding` on this component or `gap` on the parent `Column` for spacing between text blocks. - **`backgroundColor` applies to the outer ``, not the ` `.** It covers the full padded area including the padding space. - **`listStyle` is informational.** It is stored in config and consumed by the builder UI, but is not applied as CSS by this component's renderer. - **`opacity` applies to the inner ``**, not the ``. This means `backgroundColor` (on the ` `) is not affected by `opacity` — the background remains fully opaque even when `opacity < 1`. - **`textAlign` is applied both as CSS and as the HTML `align` attribute** on the ` ` for Outlook Classic compatibility. No special handling needed — set `textAlign` normally. - **Liquid template syntax is supported in `config.text`** when the platform supports it (e.g. Shopify), but only when `config.text` is not bound via `bindings.propertyMap`. Do not combine Liquid and data bindings on the same property. --- ## Quick Reference Card | Do ✅ | Don't ❌ | |------|---------| | `text` | `text` | | `text` | `text` | | `text` | `text` | | `text` | `text` | | `link` (manual color) | Worrying that manual styles will be stripped | | `link` (relies on injection) | Adding unsupported CSS properties | | Use separate ` ` tags for line breaks | `
` tags | | Combine spans: `` | Nest spans unnecessarily | | Let `injectLinkStyles` fill missing `color` and `text-decoration` | Assuming injection overrides your manual styles | | `{{ customer.first_name }}` in `text` (Shopify context, no bindings) | Liquid syntax when `config.text` is data-bound | --- ## Technical Reference: `injectLinkStyles` Behavior For implementers and advanced users, here is the exact behavior of `injectLinkStyles`: ```typescript // Properties that injectLinkStyles manages const BROWSER_LINK_DEFAULTS = ["color", "text-decoration"]; // Resolution logic for each anchor: // 1. If property exists in anchor's inline style → preserve it (skip injection) // 2. Else if property can be inherited from parent elements → use inherited value // 3. Else if fallback exists from text config → use fallback // 4. Else if property === "text-decoration" → default to "none" // 5. Else → leave undefined (property not added) ``` **This means:** - Your manual `color` and `text-decoration` are **sacred** — never touched - Only links missing these properties receive automatic values - Inheritance from parent `` elements is respected - The text config's `color` and `textDecoration` act as fallbacks, not overrides