# Heading Component — LLM Reference `Heading` renders a semantic HTML heading (`

` to `

`) with full text styling support. It is optimized for email clients with a table wrapper for reliable padding, background, and alignment. --- ## Position in the Document Tree ``` DocumentTree └── Section └── Container └── Column └── Heading ← semantic heading (h1–h6) └── Spacer └── Text ``` `Heading` is a leaf node. It can be placed anywhere a child component is accepted. --- ## ComponentNode JSON Structure ```json { "id": "unique-string-id", "type": "HeadingComponent", "config": { "text": "Welcome to our newsletter", "level": "h1", "color": "#1f2937", "fontSize": "32px" } } ``` --- ## `HeadingConfig` — Full Property Reference | Property | Type | Required | Default | Description | |--------------------|-----------------------------------|----------|-----------|-------------| | `text` | `string` (HTML) | No | — | Heading content. See HTML restrictions below. | | `level` | `"h1" \| "h2" \| "h3" \| "h4" \| "h5" \| "h6"` | No | `"h1"` | Semantic HTML heading level | | `padding` | `string` | No | — | Padding around the heading (e.g. `"20px 0"`) | | `color` | `string` | No | — | Text color (hex recommended) | | `textAlign` | `"left" \| "center" \| "right" \| "justify"` | No | — | Horizontal alignment | | `fontFamily` | `string` | No | — | Font family stack | | `fontSize` | `string` | No | — | Font size (e.g. `"28px"`) | | `fontWeight` | `string` | No | — | Font weight (`"400"`, `"700"`, `"bold"`, etc.) | | `fontStyle` | `string` | No | — | `"italic"` or `"normal"` | | `lineHeight` | `string` | No | — | Line height (e.g. `"1.3"` or `"34px"`) | | `letterSpacing` | `string` | No | — | Letter spacing (e.g. `"0.5px"`) | | `textTransform` | `string` | No | — | `"uppercase"`, `"capitalize"`, etc. | | `textDecoration` | `string` | No | — | `"underline"`, `"line-through"` | | `backgroundColor` | `string` | No | — | Background color of the block | | `verticalAlign` | `string` | No | `"top"` | Vertical alignment in the cell | | `wordBreak` | `string` | No | — | Word break behavior | | `whiteSpace` | `string` | No | — | White space handling | --- ## HTML Restrictions for `text` Property The `text` property accepts a limited subset of HTML to ensure consistent rendering across email clients and proper inheritance of parent styles. ### Allowed HTML Elements | Element | Restriction | |---------|-------------| | `` | Only allowed inline element. Must use inline `style` attributes for styling. | | `` | For links only. Use `href` attribute. See Link Style Injection below. | ### Forbidden HTML Elements The following elements are **NOT** supported and should be avoided: - ``, `` — use `` instead - ``, `` — use `` instead - `` — use `` instead - `

`-`

` (nesting headings) - `
`, `

`, `
` - Any block-level elements ### Allowed Inline CSS Properties Within `` and `
` tags, only these CSS properties are supported: | Property | Example | |----------|---------| | `font-weight` | `font-weight: 700` | | `font-style` | `font-style: italic` | | `text-decoration` | `text-decoration: underline` | | `color` | `color: #ff0000` | ### Correct Examples ```html Important announcement Bold and italic Click here ``` ### Incorrect Examples ```html Text Text Text

Text

Text

``` --- ## 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_heading", "type": "HeadingComponent", "config": { "text": "Hi {{ customer.first_name }}, your order is confirmed", "level": "h1", "fontSize": "32px", "color": "#1f2937", "textAlign": "center" } } ``` **Variable with filter:** ```json { "id": "order_heading", "type": "HeadingComponent", "config": { "text": "Order {{ order.name }} — {{ order.total_price | money }}", "level": "h2", "fontSize": "24px", "color": "#111827" } } ``` **Conditional content:** ```json { "id": "personalized_heading", "type": "HeadingComponent", "config": { "text": "{% if customer.first_name %}Welcome back, {{ customer.first_name }}!{% else %}Welcome!{% endif %}", "level": "h1", "fontSize": "28px", "color": "#1f2937", "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 allowed 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. --- ## Link Style Injection (`injectLinkStyles`) The `Heading` 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` contains HTML, the component runs the string 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 heading's config - 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 heading config (fallback). If heading 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 heading config (or defaults to `"none"`) **Example 3: Manual `text-decoration` only** ```html Click here ``` Result: - `text-decoration: none` → **preserved** (manual wins) - `color` → injected from heading 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 heading 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 heading: ```html Link that inherits heading styles ``` **✅ You can mix both approaches** — let the component handle some properties while manually controlling others: ```html Bold link that inherits color from heading ``` --- ## How It Works - Renders inside a `` → `
` wrapper for consistent padding, background, and width control across email clients. - Default browser margins on heading tags are reset (`margin: 0`). - Only supports `` and `` inline tags within the `text` property. - `injectLinkStyles` processes all `` tags to apply missing `color` and `text-decoration` styles only when not manually defined. - Uses inline styles for maximum email client compatibility. - `msoLineHeightRule: exactly` is applied for better Outlook rendering. --- ## React Usage ```tsx import Heading, { HeadingConfig } from './Heading'; const config: HeadingConfig = { text: 'Our Latest Updates', level: "h2", fontSize: "28px", color: "#111827", textAlign: "center" }; ``` The component is memoized via `arePropsEqual`. --- ## Common Patterns & Examples ### 1. Main hero heading with bold span ```json { "id": "hero_heading", "type": "HeadingComponent", "config": { "text": "Summer Sale is Here!", "level": "h1", "fontSize": "36px", "color": "#1f2937", "textAlign": "center", "padding": "20px 0 12px 0" } } ``` ### 2. Section heading with italic emphasis ```json { "id": "section_title", "type": "HeadingComponent", "config": { "text": "Featured Products", "level": "h2", "fontSize": "24px", "fontWeight": "700", "color": "#374151", "padding": "32px 0 16px 0" } } ``` ### 3. Centered heading with colored span ```json { "id": "bg_heading", "type": "HeadingComponent", "config": { "text": "Special Offer", "level": "h3", "fontSize": "22px", "color": "#ffffff", "backgroundColor": "#3b82f6", "textAlign": "center", "padding": "16px 0", "fontWeight": "600" } } ``` ### 4. Link with automatic style injection (no manual styles) ```json { "id": "auto_link_heading", "type": "HeadingComponent", "config": { "text": "Visit our website for details", "level": "h2", "fontSize": "28px", "color": "#3b82f6", "textDecoration": "underline", "textAlign": "center" } } ``` The link automatically receives `color: #3b82f6` and `text-decoration: underline` because no manual styles were provided. ### 5. Link with manual style override ```json { "id": "manual_link_heading", "type": "HeadingComponent", "config": { "text": "Special offer ends soon", "level": "h2", "fontSize": "28px", "color": "#3b82f6", "textDecoration": "none", "textAlign": "center" } } ``` The link keeps `color: #ff0000` and `font-weight: 800` (manual wins). `text-decoration` is NOT injected because heading has `textDecoration: "none"` which becomes the fallback, but manual styles take precedence for color and weight. ### 6. Link inheriting from parent span ```json { "id": "inherited_link_heading", "type": "HeadingComponent", "config": { "text": "Check this deal", "level": "h3", "fontSize": "20px", "color": "#3b82f6", "textDecoration": "underline" } } ``` The link inherits `color: #00aa00` from the parent ``. `text-decoration: underline` is injected from the heading config since the link has no manual decoration. ### 7. Combined span styles on link ```json { "id": "styled_link_heading", "type": "HeadingComponent", "config": { "text": "Click now", "level": "h2", "fontSize": "28px", "color": "#ffffff", "textDecoration": "none", "backgroundColor": "#000000", "padding": "16px 0", "textAlign": "center" } } ``` The link receives `color: #ffffff` and `text-decoration: none` from injection. The inner `` adds `font-weight: 700` and `font-style: italic` on top. ### 8. Heading with innerLink (whole block clickable) ```json { "id": "clickable_heading", "type": "HeadingComponent", "config": { "text": "Shop the Collection", "level": "h2", "fontSize": "32px", "color": "#ffffff", "backgroundColor": "#000000", "textAlign": "center", "padding": "24px 0", "innerLink": { "type": "url", "url": "https://example.com/shop", "target": "_blank" } } } ``` When `innerLink` is provided, the entire heading block becomes a clickable link. `injectLinkStyles` is not involved here — the link wrapper is applied at the table level. --- ## Data Bindings `Heading` 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": "dynamic-heading", "type": "HeadingComponent", "bindings": { "visible": "section.showTitle === true", "propertyMap": { "config.text": "section.title", "config.color": "section.titleColor" } }, "config": { "text": "Fallback Heading", "level": "h2", "fontSize": "28px", "color": "#1f2937" } } ``` | 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 `HeadingConfig` field. --- ## Rules & Constraints - **Only `` and `` tags are allowed** in the `text` property. - **Use inline styles** (`style="font-weight: 700;"`) instead of semantic HTML tags like ``, ``, ``, ``, ``. - **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`). - Supported inline style properties on `` and ``: `font-weight`, `font-style`, `text-decoration`, `color`. - The `level` should always be specified for semantic correctness. - Always define `fontSize` explicitly — do not rely on default browser sizes for headings in email. - `padding` and `backgroundColor` are applied to the wrapping table cell. - Default top/bottom margins are removed — use `padding` for spacing control. - **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. - For complex HTML content requiring multiple block elements, consider using the `Text` component instead. --- ## 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 | | 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 heading 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 heading's `color` and `textDecoration` act as fallbacks, not overrides