@reactwright/template-letter 0.1.0
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/LICENSE +21 -0
- package/README.md +105 -0
- package/package.json +43 -0
- package/src/index.ts +7 -0
- package/src/template.tsx +243 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Reactwright contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# @reactwright/template-letter
|
|
2
|
+
|
|
3
|
+
Formal business-letter template for the Reactwright document engine.
|
|
4
|
+
Single page (typically), US Letter, Times 11pt block-style body.
|
|
5
|
+
Composes the conventional regions of a formal letter via section
|
|
6
|
+
roles: letterhead, date, addressee, subject, salutation, body,
|
|
7
|
+
closing, signature.
|
|
8
|
+
|
|
9
|
+
## When to use this over alternatives
|
|
10
|
+
|
|
11
|
+
- Pick `@reactwright/template-letter` for cover letters, formal
|
|
12
|
+
business correspondence, legal-style letters, or anything that
|
|
13
|
+
wants the conventional letterhead-date-addressee-body-closing
|
|
14
|
+
shape on a single page.
|
|
15
|
+
- For anything multi-page with sections, headings, and figures, use
|
|
16
|
+
`@reactwright/template-essay`, `@reactwright/template-report`, or
|
|
17
|
+
one of the IEEE variants.
|
|
18
|
+
|
|
19
|
+
## Format conventions
|
|
20
|
+
|
|
21
|
+
- US Letter, 1" margins all sides.
|
|
22
|
+
- Body: Times New Roman 11pt, line-height 1.3, single-spaced.
|
|
23
|
+
- Letterhead (`role="letterhead"`): sender's name as 14pt bold left-
|
|
24
|
+
aligned heading; address + contact info as 10pt left-aligned
|
|
25
|
+
paragraphs below.
|
|
26
|
+
- Date (`role="date"`): right-aligned 11pt below the letterhead.
|
|
27
|
+
- Addressee (`role="addressee"`): left-aligned 11pt block; name,
|
|
28
|
+
title, organization, address as separate paragraphs.
|
|
29
|
+
- Subject (`role="subject"`): optional bold 11pt "Re: …" line.
|
|
30
|
+
- Salutation (`role="salutation"`): "Dear …,"
|
|
31
|
+
- Body: any paragraphs *not* inside one of the named regions get
|
|
32
|
+
block-style body typography — no indent, 12pt top margin between
|
|
33
|
+
paragraphs.
|
|
34
|
+
- Closing (`role="closing"`): "Sincerely,"
|
|
35
|
+
- Signature (`role="signature"`): name + title + organization, with
|
|
36
|
+
48pt top margin to leave room for a handwritten signature.
|
|
37
|
+
|
|
38
|
+
No page numbers and no running header.
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import "reactwright/jsx";
|
|
44
|
+
import { Template } from "@reactwright/template-letter";
|
|
45
|
+
|
|
46
|
+
export { Template };
|
|
47
|
+
|
|
48
|
+
export default function MyLetter() {
|
|
49
|
+
return (
|
|
50
|
+
<document title="Letter to …" author="Sender">
|
|
51
|
+
<section role="letterhead" title="Alex Marsh">
|
|
52
|
+
<p>142 Pine Street</p>
|
|
53
|
+
<p>Carrick, NY 10001</p>
|
|
54
|
+
<p>alex@example.com · (212) 555-0142</p>
|
|
55
|
+
</section>
|
|
56
|
+
|
|
57
|
+
<section role="date" title="">
|
|
58
|
+
<p>1 October 2026</p>
|
|
59
|
+
</section>
|
|
60
|
+
|
|
61
|
+
<section role="addressee" title="">
|
|
62
|
+
<p>Dr. R. Quinlan</p>
|
|
63
|
+
<p>Department of Computer Science</p>
|
|
64
|
+
<p>State University</p>
|
|
65
|
+
<p>Albany, NY 12222</p>
|
|
66
|
+
</section>
|
|
67
|
+
|
|
68
|
+
<section role="subject" title="">
|
|
69
|
+
<p>Re: Submission for Q3 review</p>
|
|
70
|
+
</section>
|
|
71
|
+
|
|
72
|
+
<section role="salutation" title="">
|
|
73
|
+
<p>Dear Dr. Quinlan,</p>
|
|
74
|
+
</section>
|
|
75
|
+
|
|
76
|
+
<p>First body paragraph.</p>
|
|
77
|
+
<p>Second body paragraph.</p>
|
|
78
|
+
|
|
79
|
+
<section role="closing" title="">
|
|
80
|
+
<p>Sincerely,</p>
|
|
81
|
+
</section>
|
|
82
|
+
|
|
83
|
+
<section role="signature" title="">
|
|
84
|
+
<p>Alex Marsh</p>
|
|
85
|
+
<p>Independent Researcher</p>
|
|
86
|
+
</section>
|
|
87
|
+
</document>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Implementation notes
|
|
93
|
+
|
|
94
|
+
Each named region is a `<section role="…">`. The template's rules use
|
|
95
|
+
the `within: { kind: "section", role: "<name>" }` selector to scope
|
|
96
|
+
typography. Body paragraphs are anything *not* inside one of the
|
|
97
|
+
named-role sections, so the author can intersperse plain `<p>` blocks
|
|
98
|
+
between the salutation and the closing without needing to wrap them
|
|
99
|
+
in a body region.
|
|
100
|
+
|
|
101
|
+
Use `title=""` on each region to suppress the section-heading; the
|
|
102
|
+
template does not emit one.
|
|
103
|
+
|
|
104
|
+
Styling is expressed entirely via the styling dialect (`<styles>` +
|
|
105
|
+
`<rule>`). The exported `LETTER_CSS` is an empty string.
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reactwright/template-letter",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Formal business letter template for the Reactwright document engine.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Reactwright contributors",
|
|
7
|
+
"homepage": "https://github.com/PurpleReverie/reactwright/tree/main/packages/template-letter#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/PurpleReverie/reactwright.git",
|
|
11
|
+
"directory": "packages/template-letter"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/PurpleReverie/reactwright/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": ["reactwright", "template", "letter", "correspondence", "business"],
|
|
17
|
+
"type": "module",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./src/index.ts",
|
|
21
|
+
"default": "./src/index.ts"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"src",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"check": "tsc --noEmit"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"reactwright": "workspace:*",
|
|
34
|
+
"react": "^18 || ^19"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^22.15.18",
|
|
38
|
+
"@types/react": "^19.1.2",
|
|
39
|
+
"react": "^19.1.0",
|
|
40
|
+
"reactwright": "workspace:*",
|
|
41
|
+
"typescript": "^5.8.3"
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Formal business-letter helper package. Bundles a Template that
|
|
2
|
+
// styles the conventional regions of a formal letter (letterhead,
|
|
3
|
+
// date, addressee, subject, salutation, body, closing, signature)
|
|
4
|
+
// via section-role rules. Authors tag each region with `role` and
|
|
5
|
+
// the template's rules pick it up — no need for engine-specific
|
|
6
|
+
// markup.
|
|
7
|
+
export { Template, LETTER_STYLES, LETTER_CSS } from "./template.js";
|
package/src/template.tsx
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import "reactwright/jsx";
|
|
2
|
+
|
|
3
|
+
// Formal business-letter template. One page, US Letter, 1" margins,
|
|
4
|
+
// Times 11pt block-style body (no indent, paragraph spacing). The
|
|
5
|
+
// letter is composed of named regions, each tagged with `role`:
|
|
6
|
+
//
|
|
7
|
+
// role="letterhead" — sender's name + address + contact info, top
|
|
8
|
+
// of page, left-aligned modern style.
|
|
9
|
+
// role="date" — right-aligned date line below letterhead.
|
|
10
|
+
// role="addressee" — left-aligned recipient block (name, title,
|
|
11
|
+
// organization, address).
|
|
12
|
+
// role="subject" — optional bold "Re: …" line.
|
|
13
|
+
// role="salutation" — "Dear …,"
|
|
14
|
+
// role="body" — one or more paragraphs of body prose. NOT
|
|
15
|
+
// a section role; just normal author-side
|
|
16
|
+
// sections without a role get treated as body.
|
|
17
|
+
// role="closing" — "Sincerely,"
|
|
18
|
+
// role="signature" — name + title + organization. The template
|
|
19
|
+
// adds three blank lines above for the
|
|
20
|
+
// handwritten signature.
|
|
21
|
+
//
|
|
22
|
+
// No page numbers, no running headers. Single-page assumed; the
|
|
23
|
+
// engine will paginate if the body overflows.
|
|
24
|
+
//
|
|
25
|
+
// Slice-1+ dialect only. customCss is empty.
|
|
26
|
+
|
|
27
|
+
export const LETTER_STYLES = `
|
|
28
|
+
.letter-letterhead {
|
|
29
|
+
margin: 0 0 24pt 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.letter-letterhead-heading {
|
|
33
|
+
font-size: 14pt;
|
|
34
|
+
font-weight: bold;
|
|
35
|
+
font-family: 'Times New Roman', Times, serif;
|
|
36
|
+
text-align: left;
|
|
37
|
+
margin: 0 0 2pt 0;
|
|
38
|
+
text-indent: 0;
|
|
39
|
+
break: after(avoid);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.letter-letterhead-p {
|
|
43
|
+
font-size: 10pt;
|
|
44
|
+
margin: 0;
|
|
45
|
+
text-indent: 0;
|
|
46
|
+
text-align: left;
|
|
47
|
+
line-height: 1.3;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.letter-date {
|
|
51
|
+
margin: 0 0 18pt 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.letter-date-p {
|
|
55
|
+
margin: 0;
|
|
56
|
+
text-indent: 0;
|
|
57
|
+
text-align: right;
|
|
58
|
+
font-size: 11pt;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.letter-addressee {
|
|
62
|
+
margin: 0 0 18pt 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.letter-addressee-p {
|
|
66
|
+
margin: 0;
|
|
67
|
+
text-indent: 0;
|
|
68
|
+
text-align: left;
|
|
69
|
+
font-size: 11pt;
|
|
70
|
+
line-height: 1.3;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.letter-subject {
|
|
74
|
+
margin: 0 0 12pt 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.letter-subject-p {
|
|
78
|
+
margin: 0;
|
|
79
|
+
text-indent: 0;
|
|
80
|
+
text-align: left;
|
|
81
|
+
font-size: 11pt;
|
|
82
|
+
font-weight: bold;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.letter-salutation {
|
|
86
|
+
margin: 0 0 12pt 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.letter-salutation-p {
|
|
90
|
+
margin: 0;
|
|
91
|
+
text-indent: 0;
|
|
92
|
+
text-align: left;
|
|
93
|
+
font-size: 11pt;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.letter-body-p {
|
|
97
|
+
margin: 12pt 0 0 0;
|
|
98
|
+
text-indent: 0;
|
|
99
|
+
text-align: left;
|
|
100
|
+
font-size: 11pt;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.letter-closing {
|
|
104
|
+
margin: 18pt 0 0 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.letter-closing-p {
|
|
108
|
+
margin: 0;
|
|
109
|
+
text-indent: 0;
|
|
110
|
+
text-align: left;
|
|
111
|
+
font-size: 11pt;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.letter-signature {
|
|
115
|
+
margin: 48pt 0 0 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.letter-signature-p {
|
|
119
|
+
margin: 0;
|
|
120
|
+
text-indent: 0;
|
|
121
|
+
text-align: left;
|
|
122
|
+
font-size: 11pt;
|
|
123
|
+
line-height: 1.3;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.letter-code-inline {
|
|
127
|
+
background: none;
|
|
128
|
+
padding: 0;
|
|
129
|
+
border-radius: 0;
|
|
130
|
+
font-family: 'SFMono-Regular', Consolas, Menlo, monospace;
|
|
131
|
+
font-size: 0.92em;
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
|
|
135
|
+
// LETTER_CSS is empty. All styling is expressed via the dialect's
|
|
136
|
+
// <styles> block + <rule> bindings.
|
|
137
|
+
export const LETTER_CSS = "";
|
|
138
|
+
|
|
139
|
+
export function Template() {
|
|
140
|
+
return (
|
|
141
|
+
<page
|
|
142
|
+
page={{
|
|
143
|
+
size: "letter",
|
|
144
|
+
marginTop: "1in",
|
|
145
|
+
marginBottom: "1in",
|
|
146
|
+
marginLeft: "1in",
|
|
147
|
+
marginRight: "1in"
|
|
148
|
+
}}
|
|
149
|
+
typography={{
|
|
150
|
+
fontFamily: "'Times New Roman', Times, serif",
|
|
151
|
+
fontSize: "11pt",
|
|
152
|
+
lineHeight: 1.3,
|
|
153
|
+
textAlign: "left"
|
|
154
|
+
}}
|
|
155
|
+
>
|
|
156
|
+
<styles>{LETTER_STYLES}</styles>
|
|
157
|
+
|
|
158
|
+
<rule match={{ kind: "code" }} className="letter-code-inline" />
|
|
159
|
+
|
|
160
|
+
{/* Letterhead */}
|
|
161
|
+
<rule match={{ kind: "section", role: "letterhead" }} className="letter-letterhead" />
|
|
162
|
+
<rule
|
|
163
|
+
match={{ kind: "section-heading", within: { kind: "section", role: "letterhead" } }}
|
|
164
|
+
className="letter-letterhead-heading"
|
|
165
|
+
/>
|
|
166
|
+
<rule
|
|
167
|
+
match={{ kind: "paragraph", within: { kind: "section", role: "letterhead" } }}
|
|
168
|
+
className="letter-letterhead-p"
|
|
169
|
+
/>
|
|
170
|
+
|
|
171
|
+
{/* Date */}
|
|
172
|
+
<rule match={{ kind: "section", role: "date" }} className="letter-date" />
|
|
173
|
+
<rule
|
|
174
|
+
match={{ kind: "paragraph", within: { kind: "section", role: "date" } }}
|
|
175
|
+
className="letter-date-p"
|
|
176
|
+
/>
|
|
177
|
+
|
|
178
|
+
{/* Addressee */}
|
|
179
|
+
<rule match={{ kind: "section", role: "addressee" }} className="letter-addressee" />
|
|
180
|
+
<rule
|
|
181
|
+
match={{ kind: "paragraph", within: { kind: "section", role: "addressee" } }}
|
|
182
|
+
className="letter-addressee-p"
|
|
183
|
+
/>
|
|
184
|
+
|
|
185
|
+
{/* Subject */}
|
|
186
|
+
<rule match={{ kind: "section", role: "subject" }} className="letter-subject" />
|
|
187
|
+
<rule
|
|
188
|
+
match={{ kind: "paragraph", within: { kind: "section", role: "subject" } }}
|
|
189
|
+
className="letter-subject-p"
|
|
190
|
+
/>
|
|
191
|
+
|
|
192
|
+
{/* Salutation */}
|
|
193
|
+
<rule match={{ kind: "section", role: "salutation" }} className="letter-salutation" />
|
|
194
|
+
<rule
|
|
195
|
+
match={{ kind: "paragraph", within: { kind: "section", role: "salutation" } }}
|
|
196
|
+
className="letter-salutation-p"
|
|
197
|
+
/>
|
|
198
|
+
|
|
199
|
+
{/* Closing */}
|
|
200
|
+
<rule match={{ kind: "section", role: "closing" }} className="letter-closing" />
|
|
201
|
+
<rule
|
|
202
|
+
match={{ kind: "paragraph", within: { kind: "section", role: "closing" } }}
|
|
203
|
+
className="letter-closing-p"
|
|
204
|
+
/>
|
|
205
|
+
|
|
206
|
+
{/* Signature block — extra top margin to leave room for a
|
|
207
|
+
handwritten signature. */}
|
|
208
|
+
<rule match={{ kind: "section", role: "signature" }} className="letter-signature" />
|
|
209
|
+
<rule
|
|
210
|
+
match={{ kind: "paragraph", within: { kind: "section", role: "signature" } }}
|
|
211
|
+
className="letter-signature-p"
|
|
212
|
+
/>
|
|
213
|
+
|
|
214
|
+
{/* Body paragraphs — any paragraph that isn't in a named region
|
|
215
|
+
gets block-style body typography with paragraph spacing. */}
|
|
216
|
+
<rule
|
|
217
|
+
match={{
|
|
218
|
+
kind: "paragraph",
|
|
219
|
+
not: {
|
|
220
|
+
or: [
|
|
221
|
+
{ within: { kind: "section", role: "letterhead" } },
|
|
222
|
+
{ within: { kind: "section", role: "date" } },
|
|
223
|
+
{ within: { kind: "section", role: "addressee" } },
|
|
224
|
+
{ within: { kind: "section", role: "subject" } },
|
|
225
|
+
{ within: { kind: "section", role: "salutation" } },
|
|
226
|
+
{ within: { kind: "section", role: "closing" } },
|
|
227
|
+
{ within: { kind: "section", role: "signature" } }
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
}}
|
|
231
|
+
className="letter-body-p"
|
|
232
|
+
/>
|
|
233
|
+
|
|
234
|
+
<stack gap="0">
|
|
235
|
+
<region>
|
|
236
|
+
<slot name="title" />
|
|
237
|
+
<slot name="author" />
|
|
238
|
+
<slot name="body" />
|
|
239
|
+
</region>
|
|
240
|
+
</stack>
|
|
241
|
+
</page>
|
|
242
|
+
);
|
|
243
|
+
}
|