` → `#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 |