@hyper-elements/react 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GUIDE.md +47 -28
- package/README.md +24 -5
- package/REFERENCE.md +487 -485
- package/dist/builder.cjs +795 -206
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.js +795 -206
- package/dist/builder.js.map +1 -1
- package/dist/image.cjs +1 -1
- package/dist/image.cjs.map +1 -1
- package/dist/image.js +1 -1
- package/dist/image.js.map +1 -1
- package/dist/inspect.cjs +3601 -0
- package/dist/inspect.cjs.map +1 -0
- package/dist/inspect.d.cts +472 -0
- package/dist/inspect.d.ts +472 -0
- package/dist/inspect.js +3572 -0
- package/dist/inspect.js.map +1 -0
- package/dist/pdf.cjs +54 -9
- package/dist/pdf.cjs.map +1 -1
- package/dist/pdf.js +54 -9
- package/dist/pdf.js.map +1 -1
- package/dist/primitives.cjs +738 -142
- package/dist/primitives.cjs.map +1 -1
- package/dist/primitives.d.cts +558 -158
- package/dist/primitives.d.ts +558 -158
- package/dist/primitives.js +736 -143
- package/dist/primitives.js.map +1 -1
- package/package.json +10 -1
package/GUIDE.md
CHANGED
|
@@ -13,8 +13,25 @@ Everything needed to write a template, in short. Every prop's and style's detail
|
|
|
13
13
|
- Text is components, not HTML: `Heading level={1}` and `Text` hold text; inside them `Span` styles a run, `Link href` links one, `LineBreak` breaks the line and `MergeTag name` writes a merge tag. No raw HTML (`<b>`, `<br>`, `<div>`) and no `html` prop.
|
|
14
14
|
- `Image src={{ src, width, height }}` (the file's size in px) with `alt`; `style.width` sets the size shown.
|
|
15
15
|
- Lists: `<List type="number">` of `ListItem`s. Tables: a `Table` of `TableRow`s (`header` on the first) of `TableCell`s, with `cellPadding` and `rowBorder` in the Table's `style`.
|
|
16
|
-
- Phones: `
|
|
17
|
-
- `await render(element)` gives `{ html, text, diagnostics }`;
|
|
16
|
+
- Phones: `style={{ fontSize: 24, mobile: { fontSize: 20 } }}` (the styles listed under "Phones", where `text` is fontSize, lineHeight, letterSpacing and textAlign), `hidden={{ mobile: true }}`; in an Email or a Page. Hover: `style={{ hover: { backgroundColor: "#374151" } }}` (those under "Hover").
|
|
17
|
+
- `await render(element)` gives `{ html, text, diagnostics }`; errors throw an `ElementsError` with the same `diagnostics`. `validate(element)` runs the checks without rendering; `outline(element)` prints the laid-out template (each block, its width, its text).
|
|
18
|
+
- An Email's warnings name the email clients they're about (`clients`), with a `fix` and a `see` link. The clients most email is read in are checked; `emailClients: [...DEFAULT_EMAIL_CLIENTS, "gmx"]` checks more.
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { writeFile } from "node:fs/promises";
|
|
22
|
+
import { ElementsError, formatDiagnostics, render } from "@hyper-elements/react";
|
|
23
|
+
import Welcome from "./welcome";
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const { html, diagnostics } = await render(<Welcome />);
|
|
27
|
+
await writeFile("welcome.html", html);
|
|
28
|
+
console.log(formatDiagnostics(diagnostics)); // warnings, each with its email clients and fix
|
|
29
|
+
} catch (e) {
|
|
30
|
+
if (!(e instanceof ElementsError)) throw e;
|
|
31
|
+
console.error(formatDiagnostics(e.diagnostics));
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
}
|
|
34
|
+
```
|
|
18
35
|
|
|
19
36
|
## Common mistakes
|
|
20
37
|
|
|
@@ -25,7 +42,8 @@ Each of these made an agent's first try fail:
|
|
|
25
42
|
- `items={[{ text: "Home", link: "/" }]}` → `items={[{ text: "Home", href: "/" }]}`. A `Menu`'s items are `{ text, href }`, a `Social`'s icons `{ name, href }`.
|
|
26
43
|
- `style={{ marginBottom: 16 }}` → `gap: 16` on the parent. There are no margins.
|
|
27
44
|
- `ColumnLayouts`, `layout`, `headingType`, `altText`, `html` are Unlayer Elements' props, not these: a `Row` of `Column`s, `level`, `alt`, and text as children.
|
|
28
|
-
- `<Image src="https://example.com/logo.png">` → `src={{ src: "https://example.com/logo.png", width: 240, height: 80 }}`, the file's size. Without it Outlook shows the
|
|
45
|
+
- `<Image src="https://example.com/logo.png">` → `src={{ src: "https://example.com/logo.png", width: 240, height: 80 }}`, the file's size. Without it, Outlook shows the file at its own size.
|
|
46
|
+
- `mobile={{ fontSize: 20 }}`, `hover={{ … }}`, `_hover` or `md: { … }` → `style={{ fontSize: 24, mobile: { fontSize: 20 }, hover: { … } }}`. Phone and hover styles are blocks of `style`, and there's one breakpoint: phones.
|
|
29
47
|
|
|
30
48
|
## A whole template
|
|
31
49
|
|
|
@@ -37,7 +55,7 @@ export default function Receipt() {
|
|
|
37
55
|
<Email previewText="Your Acme receipt" style={{ backgroundColor: "#f4f4f5", fontFamily: "Inter, Arial, sans-serif", gap: 16 }}>
|
|
38
56
|
<Image src={{ src: "https://example.com/logo.png", width: 240, height: 80 }} alt="Acme" style={{ width: 120 }} />
|
|
39
57
|
<Column style={{ backgroundColor: "#ffffff", padding: 32, gap: 16, borderRadius: 8 }}>
|
|
40
|
-
<Heading level={1} style={{ fontSize: 24
|
|
58
|
+
<Heading level={1} style={{ fontSize: 24, mobile: { fontSize: 20 } }}>
|
|
41
59
|
Thanks for your order, <MergeTag name="first_name" />
|
|
42
60
|
</Heading>
|
|
43
61
|
<Text>
|
|
@@ -66,7 +84,7 @@ export default function Receipt() {
|
|
|
66
84
|
</List>
|
|
67
85
|
</Column>
|
|
68
86
|
</Row>
|
|
69
|
-
<Button href="https://example.com/orders/10482" style={{ backgroundColor: "#111827", borderRadius: 6
|
|
87
|
+
<Button href="https://example.com/orders/10482" style={{ backgroundColor: "#111827", borderRadius: 6, hover: { backgroundColor: "#374151" } }}>
|
|
70
88
|
View your order
|
|
71
89
|
</Button>
|
|
72
90
|
</Column>
|
|
@@ -78,7 +96,7 @@ export default function Receipt() {
|
|
|
78
96
|
|
|
79
97
|
## A Canvas as an image
|
|
80
98
|
|
|
81
|
-
`renderImage(element, options)` (`@hyper-elements/react/image`) writes a `Canvas` as a PNG (`format: "svg"` an SVG, `scale: 2` twice the pixels). Nothing is fetched: `fonts` gives each font file by the family the template names (TTF, OTF or WOFF, not WOFF2; one per weight and style used), `images` each image file by the `src` the template gives. It gives `{ image, width, height, diagnostics }` (what it drew otherwise
|
|
99
|
+
`renderImage(element, options)` (`@hyper-elements/react/image`) writes a `Canvas` as a PNG (`format: "svg"` an SVG, `scale: 2` twice the pixels). Nothing is fetched: `fonts` gives each font file by the family the template names (TTF, OTF or WOFF, not WOFF2; one per weight and style used), `images` each image file by the `src` the template gives. It gives `{ image, width, height, diagnostics }` (what it drew otherwise: a weight not given, a hover); errors throw an `ElementsError`.
|
|
82
100
|
|
|
83
101
|
```tsx
|
|
84
102
|
import { readFile } from "node:fs/promises";
|
|
@@ -103,29 +121,29 @@ const { image, diagnostics } = await renderImage(
|
|
|
103
121
|
|
|
104
122
|
### Roots
|
|
105
123
|
|
|
106
|
-
- **Email**: An email. Its children are stacked down the content width, centered in the window. Holds sections, layout, blocks. Props: width (a length (a number of px)), previewText (text), title (text), lang (text), dir (ltr|rtl|auto), fonts (fontFaces). Style: text, backgroundColor, gap, alignItems (start|center|end|stretch). Phones:
|
|
107
|
-
- **Page**: A web page. Its children are stacked down the content width, centered in the window. Holds sections, layout, blocks. Props: width (a length (a number of px)), description (text), title (text), lang (text), dir (ltr|rtl|auto), fonts (fontFaces). Style: text, backgroundColor, gap, alignItems (start|center|end|stretch). Phones:
|
|
108
|
-
- **Document**: A printed document: its children on pages of its size (Document.Page, or split by PageBreaks), a PageHeader and a PageFooter on each. Renders to print HTML, and to PDF with @hyper-elements/react/pdf. Holds sections, layout, blocks, Document.Page, PageBreak, PageHeader, PageFooter. Props: size (A3|A4|A5|Letter|Legal|Tabloid), orientation (portrait|landscape), margin (insets), title (text), lang (text), dir (ltr|rtl|auto), fonts (fontFaces). Style: text, backgroundColor, gap, alignItems (start|center|end|stretch).
|
|
109
|
-
- **Canvas**: An image of a fixed size (a social card, a banner): its children laid out in it as on a page. Written as PNG or SVG with @hyper-elements/react/image. Holds sections, layout, blocks. Props: width (a length (a number of px)), height (a length (a number of px)), title (text), lang (text), dir (ltr|rtl|auto), fonts (fontFaces). Style: text, backgroundColor, gap, alignItems (start|center|end|stretch), padding, justifyContent (start|center|end|space-between).
|
|
124
|
+
- **Email**: An email. Its children are stacked down the content width, centered in the window. Holds sections, layout, blocks. Props: width (a length (a number of px)), previewText (text), title (text), lang (text), dir (ltr|rtl|auto), fonts (fontFaces). Style: text, backgroundColor, gap, alignItems (start|center|end|stretch). Phones: text, gap.
|
|
125
|
+
- **Page**: A web page. Its children are stacked down the content width, centered in the window. Holds sections, layout, blocks. Props: width (a length (a number of px)), description (text), breakpoints (breakpoints), title (text), lang (text), dir (ltr|rtl|auto), fonts (fontFaces). Style: text, backgroundColor, gap, alignItems (start|center|end|stretch). Phones: gap.
|
|
126
|
+
- **Document**: A printed document: its children on pages of its size (Document.Page, or split by PageBreaks), a PageHeader and a PageFooter on each. Renders to print HTML, and to PDF with @hyper-elements/react/pdf. Holds sections, layout, blocks, Document.Page, PageBreak, PageHeader, PageFooter. Props: size (A3|A4|A5|Letter|Legal|Tabloid), orientation (portrait|landscape), margin (insets), title (text), lang (text), dir (ltr|rtl|auto), fonts (fontFaces). Style: text, backgroundColor, gap, alignItems (start|center|end|stretch).
|
|
127
|
+
- **Canvas**: An image of a fixed size (a social card, a banner): its children laid out in it as on a page. Written as PNG or SVG with @hyper-elements/react/image. Holds sections, layout, blocks. Props: width (a length (a number of px)), height (a length (a number of px)), title (text), lang (text), dir (ltr|rtl|auto), fonts (fontFaces). Style: text, backgroundColor, gap, alignItems (start|center|end|stretch), padding, justifyContent (start|center|end|space-between).
|
|
110
128
|
|
|
111
129
|
### Sections and layout
|
|
112
130
|
|
|
113
|
-
- **Section**: A band across the whole window, directly in an Email or Page: its background spans the window, its children are stacked at the content width. Holds layout, blocks. Style: text, backgroundColor, padding, border, gap, alignItems (start|center|end|stretch). Phones:
|
|
114
|
-
- **Row**: Lays its children out side by side. A child takes its width, or a share of what's left (its flex, 1 by default). Holds layout, blocks. Props: stackOnMobile (true or false), dir (ltr|rtl|auto). Style: text, box, minWidth, height, overflow (visible|hidden), gap, alignItems (start|center|end|stretch), justifyContent (start|center|end|space-between), flexWrap (nowrap|wrap|words), flex. Phones:
|
|
115
|
-
- **Column**: Stacks its children vertically, with its own spacing and box. Holds layout, blocks. Style: text, box, minWidth, height, overflow (visible|hidden), gap, alignItems (start|center|end|stretch), justifyContent (start|center|end|space-between), flex. Phones:
|
|
116
|
-
- **Grid**: Its children in columns of equal width, row after row; on phones, one under another. Holds layout, blocks. Props: columns (an integer from 1 to 12), stackOnMobile (true or false). Style: text, box, minWidth, height, overflow (visible|hidden), gap, rowGap, alignItems (start|center|end|stretch), flex. Phones:
|
|
117
|
-
- **Layer**: Its children over each other, in one box as tall as the tallest: an image
|
|
131
|
+
- **Section**: A band across the whole window, directly in an Email or Page: its background spans the window, its children are stacked at the content width. Holds layout, blocks. Style: text, backgroundColor, padding, border, gap, alignItems (start|center|end|stretch). Phones: text, padding, border, gap.
|
|
132
|
+
- **Row**: Lays its children out side by side. A child takes its width, or a share of what's left (its flex, 1 by default). Holds layout, blocks. Props: stackOnMobile (true or false), dir (ltr|rtl|auto). Style: text, box, minWidth, height, overflow (visible|hidden), gap, alignItems (start|center|end|stretch), justifyContent (start|center|end|space-between), flexWrap (nowrap|wrap|words), flex. Phones: text, width, maxWidth, padding, border, gap.
|
|
133
|
+
- **Column**: Stacks its children vertically, with its own spacing and box. Holds layout, blocks. Style: text, box, minWidth, height, overflow (visible|hidden), gap, alignItems (start|center|end|stretch), justifyContent (start|center|end|space-between), flex. Phones: text, width, maxWidth, padding, border, gap.
|
|
134
|
+
- **Grid**: Its children in columns of equal width, row after row; on phones, one under another. Holds layout, blocks. Props: columns (an integer from 1 to 12), stackOnMobile (true or false). Style: text, box, minWidth, height, overflow (visible|hidden), gap, rowGap, alignItems (start|center|end|stretch), flex. Phones: text, width, maxWidth, padding, border, gap, rowGap.
|
|
135
|
+
- **Layer**: Its children over each other, in one box as tall as the tallest: words on an image (a background image, its first child). In an email, an Image with one child over it. Holds layout, blocks. Style: text, width, minWidth, maxWidth, backgroundColor, border, borderRadius, height, alignItems (start|center|end|stretch), justifyItems (start|center|end|stretch), flex. Phones: text, width, maxWidth, border.
|
|
118
136
|
|
|
119
137
|
### Blocks
|
|
120
138
|
|
|
121
|
-
- **Svg**: Shapes (Rect, Circle, Ellipse, Line, Polygon, Path) drawn in a box of its own: its width and height, and the coordinates its viewBox gives them. Holds shapes. Props: width* (a number from 0), height* (a number from 0), viewBox (text), title (text). Style: padding, backgroundColor, border, borderRadius, flex. Phones: padding, border.
|
|
122
|
-
- **Text**: A paragraph of text. Holds text, inline, Image. Props: dir (ltr|rtl|auto). Style: text, box, textDecoration (none|underline|line-through), flex. Phones:
|
|
123
|
-
- **Heading**: A heading, from level 1 (the page's title) to 6. Holds text, inline. Props: level (1|2|3|4|5|6), dir (ltr|rtl|auto). Style: text, box, textDecoration (none|underline|line-through), flex. Phones:
|
|
124
|
-
- **Button**: A link drawn as a button. Holds text, Span, MergeTag, LineBreak. Props: href* (link), title (text), aria-label (text), attrs (attributes). Style: text, box, textDecoration (none|underline|line-through), boxSizing (border-box|content-box), alignSelf (start|center|end), flex. Hover: color, backgroundColor. Phones:
|
|
139
|
+
- **Svg**: Shapes (Rect, Circle, Ellipse, Line, Polygon, Path) drawn in a box of its own: its width and height, and the coordinates its viewBox gives them. Not in an Email. Holds shapes. Props: width* (a number from 0), height* (a number from 0), viewBox (text), title (text). Style: padding, backgroundColor, border, borderRadius, flex. Phones: padding, border.
|
|
140
|
+
- **Text**: A paragraph of text. Holds text, inline, Image. Props: dir (ltr|rtl|auto). Style: text, box, textDecoration (none|underline|line-through), flex. Phones: text, width, maxWidth, padding, border.
|
|
141
|
+
- **Heading**: A heading, from level 1 (the page's title) to 6. Holds text, inline. Props: level (1|2|3|4|5|6), dir (ltr|rtl|auto). Style: text, box, textDecoration (none|underline|line-through), flex. Phones: text, width, maxWidth, padding, border.
|
|
142
|
+
- **Button**: A link drawn as a button. Holds text, Span, MergeTag, LineBreak. Props: href* (link), title (text), aria-label (text), attrs (attributes). Style: text, box, textDecoration (none|underline|line-through), boxSizing (border-box|content-box), alignSelf (start|center|end), flex. Hover: color, backgroundColor. Phones: text, width, maxWidth, padding, border, alignSelf.
|
|
125
143
|
- **Image**: An image in the content. Without a width it takes its own size, no wider than its parent. Props: src* (asset), alt (text), title (text). Style: width, maxWidth, height, border, borderRadius, flex, alignSelf (start|center|end), verticalAlign (top|middle). Phones: width, maxWidth, border, alignSelf.
|
|
126
144
|
- **Divider**: A horizontal line. Style: borderTop, width, padding. Phones: width, padding.
|
|
127
|
-
- **List**: A bulleted or numbered list of ListItems. Its markers sit in a column of their own (indent) before the items. Holds ListItem. Props: type (bullet|number), start (an integer from 0). Style: text, width, maxWidth, padding, backgroundColor, border, gap, indent, flex. Phones:
|
|
128
|
-
- **Table**: A table of data: TableRows of TableCells. Columns take their cells' widths; the rest share what's left. Holds TableRow. Style: text, width, maxWidth, backgroundColor, border, cellPadding, rowBorder, borderCollapse (separate|collapse), flex. Phones:
|
|
145
|
+
- **List**: A bulleted or numbered list of ListItems. Its markers sit in a column of their own (indent) before the items. Holds ListItem. Props: type (bullet|number), start (an integer from 0). Style: text, width, maxWidth, padding, backgroundColor, border, gap, indent, flex. Phones: text, width, maxWidth, padding, border, gap.
|
|
146
|
+
- **Table**: A table of data: TableRows of TableCells. Columns take their cells' widths; the rest share what's left. Holds TableRow. Style: text, width, maxWidth, backgroundColor, border, cellPadding, rowBorder, borderCollapse (separate|collapse), flex. Phones: text, width, maxWidth, border.
|
|
129
147
|
- **Video**: A YouTube or Vimeo video: its player on a page; in an email (which can't play one), its thumbnail, linked to it. Props: src* (video), thumbnail (asset), title (text). Style: width, maxWidth, flex. Phones: width, maxWidth.
|
|
130
148
|
- **Social**: Links to social profiles: a line of the networks' icons, each linked to its profile. Props: icons* (a list of icons, each { name, href }), iconType (circle|rounded|squared), size (an integer from 1). Style: gap, justifyContent (start|center|end), flex. Phones: gap.
|
|
131
149
|
- **Menu**: A line of links (a site's sections, a footer's), onto more lines where they don't fit, with a separator between them on desktop; or links down a column. Props: items* (a list of items, each { text, href }), separator (text), layout (horizontal|vertical). Style: color, fontFamily, fontSize, fontWeight, lineHeight, letterSpacing, textTransform (none|uppercase|lowercase|capitalize), separatorColor, padding, justifyContent (start|center|end), flex. Phones: fontSize, lineHeight, letterSpacing.
|
|
@@ -133,7 +151,7 @@ const { image, diagnostics } = await renderImage(
|
|
|
133
151
|
|
|
134
152
|
### Inline
|
|
135
153
|
|
|
136
|
-
- **PageNumber**: In a Document's text: the number of the page it's on, or (total) how many pages there are. Props: total (true or false).
|
|
154
|
+
- **PageNumber**: In a Document's text: the number of the page it's on, or (total) how many pages there are. Only in a Document. Props: total (true or false).
|
|
137
155
|
- **Span**: Styled text inside text; raised or lowered (superscript, subscript) with verticalAlign. Holds text, inline. Style: color, fontFamily, fontSize, fontWeight, fontStyle (normal|italic), letterSpacing, textTransform (none|uppercase|lowercase|capitalize), textDecoration (none|underline|line-through), backgroundColor, verticalAlign (baseline|super|sub). Phones: fontSize, letterSpacing.
|
|
138
156
|
- **Link**: A link: text inside text, or around an Image. Holds text, inline, Image. Props: href* (link), title (text), aria-label (text), attrs (attributes). Style: color, fontFamily, fontSize, fontWeight, fontStyle (normal|italic), letterSpacing, textTransform (none|uppercase|lowercase|capitalize), textDecoration (none|underline|line-through), backgroundColor, flex. Hover: color, textDecoration, backgroundColor. Phones: fontSize, letterSpacing.
|
|
139
157
|
- **LineBreak**: Starts a new line inside text.
|
|
@@ -141,13 +159,13 @@ const { image, diagnostics } = await renderImage(
|
|
|
141
159
|
|
|
142
160
|
### Parts
|
|
143
161
|
|
|
144
|
-
- **Document.Page**: One page of a Document (Document.Page in JSX): its children stacked down it, inside the Document's margins. Holds sections, layout, blocks. Style: text, backgroundColor, gap, alignItems (start|center|end|stretch).
|
|
162
|
+
- **Document.Page**: One page of a Document (Document.Page in JSX): its children stacked down it, inside the Document's margins. Holds sections, layout, blocks. Style: text, backgroundColor, gap, alignItems (start|center|end|stretch).
|
|
145
163
|
- **PageBreak**: In a Document, where a page ends: what follows starts the next page.
|
|
146
|
-
- **PageHeader**: In a Document: drawn at the top of every page, inside its margins. Holds layout, blocks. Style: text, padding, backgroundColor, border, gap, alignItems (start|center|end|stretch).
|
|
147
|
-
- **PageFooter**: In a Document: drawn at the bottom of every page, inside its margins. Holds layout, blocks. Style: text, padding, backgroundColor, border, gap, alignItems (start|center|end|stretch).
|
|
148
|
-
- **ListItem**: An item of a List: text, and a List of its own to nest one. Holds text, inline, List. Props: dir (ltr|rtl|auto). Style: text, textDecoration (none|underline|line-through). Phones:
|
|
149
|
-
- **TableRow**: A row of a Table. A header row's cells are header cells, in bold. Holds TableCell. Props: header (true or false), footer (true or false). Style: text, backgroundColor, height. Phones:
|
|
150
|
-
- **TableCell**: A cell of a TableRow: text, and images. Its width sets its column's. Holds text, inline, Image. Props: colSpan (an integer from 1), dir (ltr|rtl|auto). Style: text, textDecoration (none|underline|line-through), padding, backgroundColor, border, width, verticalAlign (top|middle|bottom). Phones:
|
|
164
|
+
- **PageHeader**: In a Document: drawn at the top of every page, inside its margins. Holds layout, blocks. Style: text, padding, backgroundColor, border, gap, alignItems (start|center|end|stretch).
|
|
165
|
+
- **PageFooter**: In a Document: drawn at the bottom of every page, inside its margins. Holds layout, blocks. Style: text, padding, backgroundColor, border, gap, alignItems (start|center|end|stretch).
|
|
166
|
+
- **ListItem**: An item of a List: text, and a List of its own to nest one. Holds text, inline, List. Props: dir (ltr|rtl|auto). Style: text, textDecoration (none|underline|line-through). Phones: text.
|
|
167
|
+
- **TableRow**: A row of a Table. A header row's cells are header cells, in bold. Holds TableCell. Props: header (true or false), footer (true or false). Style: text, backgroundColor, height. Phones: text.
|
|
168
|
+
- **TableCell**: A cell of a TableRow: text, and images. Its width sets its column's. Holds text, inline, Image. Props: colSpan (an integer from 1), dir (ltr|rtl|auto). Style: text, textDecoration (none|underline|line-through), padding, backgroundColor, border, width, verticalAlign (top|middle|bottom). Phones: text, padding, border.
|
|
151
169
|
|
|
152
170
|
### Shapes
|
|
153
171
|
|
|
@@ -165,6 +183,7 @@ const { image, diagnostics } = await renderImage(
|
|
|
165
183
|
- *fontFamily*: a font stack, as CSS writes it ("Inter, Arial, sans-serif")
|
|
166
184
|
- *fontWeight*: 100 to 900 in hundreds, "normal" or "bold"
|
|
167
185
|
- *lineHeight*: a multiple of the font size (1.5), a percentage ("150%"), px ("24px") or "normal" (the font's own)
|
|
186
|
+
- *breakpoints*: { mobile }: the phone width in px (from 240)
|
|
168
187
|
- *insets*: px for all sides, "8px 16px" (1 to 4 values), [v, h] or [t, r, b, l], or { top, right, bottom, left }
|
|
169
188
|
- *border*: "1px solid #ccc", { width, style, color } for all sides, or { top, right, bottom, left } with one of those each
|
|
170
189
|
- *radius*: px for every corner, [topLeft, topRight, bottomRight, bottomLeft], or { topLeft, topRight, bottomRight, bottomLeft }
|
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ export default function Receipt() {
|
|
|
22
22
|
<Email previewText="Your Acme receipt" style={{ backgroundColor: "#f4f4f5", fontFamily: "Inter, Arial, sans-serif", gap: 16 }}>
|
|
23
23
|
<Image src={{ src: "https://example.com/logo.png", width: 240, height: 80 }} alt="Acme" style={{ width: 120 }} />
|
|
24
24
|
<Column style={{ backgroundColor: "#ffffff", padding: 32, gap: 16, borderRadius: 8 }}>
|
|
25
|
-
<Heading level={1} style={{ fontSize: 24
|
|
25
|
+
<Heading level={1} style={{ fontSize: 24, mobile: { fontSize: 20 } }}>
|
|
26
26
|
Thanks for your order, <MergeTag name="first_name" />
|
|
27
27
|
</Heading>
|
|
28
28
|
<Text>
|
|
@@ -51,7 +51,7 @@ export default function Receipt() {
|
|
|
51
51
|
</List>
|
|
52
52
|
</Column>
|
|
53
53
|
</Row>
|
|
54
|
-
<Button href="https://example.com/orders/10482" style={{ backgroundColor: "#111827", borderRadius: 6
|
|
54
|
+
<Button href="https://example.com/orders/10482" style={{ backgroundColor: "#111827", borderRadius: 6, hover: { backgroundColor: "#374151" } }}>
|
|
55
55
|
View your order
|
|
56
56
|
</Button>
|
|
57
57
|
</Column>
|
|
@@ -221,15 +221,17 @@ const printed = await render(<Document size="A4" margin={48}><Invoice /></Docume
|
|
|
221
221
|
|
|
222
222
|
## Phones and hover
|
|
223
223
|
|
|
224
|
-
`mobile` holds a component's styles on phones
|
|
224
|
+
Inside `style`, `mobile` holds a component's styles on phones and `hover` its styles under the pointer; `hidden` shows it on one size only. Each component's list is in [GUIDE.md](./GUIDE.md).
|
|
225
225
|
|
|
226
226
|
```tsx
|
|
227
|
-
<Heading style={{ fontSize: 32
|
|
227
|
+
<Heading style={{ fontSize: 32, mobile: { fontSize: 24, textAlign: "center" } }}>Welcome</Heading>
|
|
228
228
|
<Text hidden={{ mobile: true }}>Desktop detail</Text>
|
|
229
229
|
<Text hidden={{ desktop: true }}>Phone detail</Text>
|
|
230
|
-
<Button href="https://example.com" style={{ backgroundColor: "#111827"
|
|
230
|
+
<Button href="https://example.com" style={{ backgroundColor: "#111827", hover: { backgroundColor: "#374151" } }}>Buy</Button>
|
|
231
231
|
```
|
|
232
232
|
|
|
233
|
+
Phones are at or below an email's width plus 20px, and at or below 480px on a page, or its own width: `<Page breakpoints={{ mobile: 600 }}>`. Hover styles apply on keyboard focus too.
|
|
234
|
+
|
|
233
235
|
## Common design patterns
|
|
234
236
|
|
|
235
237
|
### Header with a logo
|
|
@@ -338,6 +340,23 @@ const { element, code, report: read } = fromDesign(saved, { displayMode: "email"
|
|
|
338
340
|
|
|
339
341
|
The design holds no Hyper Elements data. A style or prop a builder tool has no field for is a hidden property of that tool, kept in the design under its own name and drawn by Hyper Elements, with no editor in the builder yet. What the builder can't arrange comes back with the same look, flatter: a Section around several Rows as a Section each, a box's padding on its blocks, a Grid as Rows of Columns. What no tool shows (a Layer, an Svg, a Table, a box nested in a column, your own `component()`) goes as an HTML block of the new engine's markup, and comes back as an `Html` block, as the builder's own HTML blocks do. A design's markup comes back as it is: where designs can hold untrusted content, pass your sanitizer, `fromDesign(saved, { toSafeHtml })`. A line height goes in the builder's field as a whole percentage, as browsers draw it, so `24px` at 13px is `185%`, a twentieth of a pixel taller there. The builder has no page numbers yet: a `PageNumber` goes as its token, which the editor's PDF prints as it is, and `report` says so.
|
|
340
342
|
|
|
343
|
+
## What each root takes
|
|
344
|
+
|
|
345
|
+
Not everything goes in every root: an `Svg` isn't in an Email, a `PageNumber` is only in a Document, and phone styles are only in an Email or a Page. `@hyper-elements/react/inspect` says which, as data, for an agent or an editor: before writing a template, what a root takes; after, whether each part of it is allowed where it is.
|
|
346
|
+
|
|
347
|
+
```tsx
|
|
348
|
+
import { describeComponent, describeComponents, inspect } from "@hyper-elements/react/inspect";
|
|
349
|
+
|
|
350
|
+
describeComponents({ root: "Email" }); // the components an Email takes, each prop and style with its kind, group and default
|
|
351
|
+
describeComponent("Svg", { root: "Email" }); // undefined: an Email doesn't take one
|
|
352
|
+
describeComponent("Svg").roots; // ["Page", "Document", "Canvas"]
|
|
353
|
+
|
|
354
|
+
const { valid, nodes } = inspect(<Welcome />);
|
|
355
|
+
// nodes: each component, in order: { path, type, roots, allowed, given: ["style.padding", "mobile.padding"], problems }
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
`inspect()` checks what `validate()` checks, without rendering, and puts each problem on the component it's on. Your own components (`component()`) are described with the built-in ones: `describeComponents({ components: [ProductCard] })`.
|
|
359
|
+
|
|
341
360
|
## Task guides and runnable examples
|
|
342
361
|
|
|
343
362
|
- [Transactional email in React](https://github.com/hyper-elements/hyper-elements/blob/main/packages/react/docs/transactional-email.md): render HTML and plain text, then send a receipt.
|