jsonresume-theme-academic-cv-lite 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/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "jsonresume-theme-academic-cv-lite",
3
+ "version": "0.1.0",
4
+ "description": "Academic professionals - publications matter most. Scholarly, formal, elegant simplicity.",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "jsonresume",
10
+ "theme",
11
+ "academic",
12
+ "cv",
13
+ "scholarly",
14
+ "publications"
15
+ ],
16
+ "peerDependencies": {
17
+ "react": "^18.0.0 || ^19.0.0",
18
+ "react-dom": "^18.0.0 || ^19.0.0"
19
+ },
20
+ "dependencies": {
21
+ "styled-components": "^6.1.19",
22
+ "@resume/core": "0.1.0"
23
+ }
24
+ }
package/src/Resume.jsx ADDED
@@ -0,0 +1,447 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { Section, SectionTitle, DateRange, ContactInfo } from '@resume/core';
4
+
5
+ const Layout = styled.div`
6
+ max-width: 800px;
7
+ margin: 0 auto;
8
+ padding: 50px 40px;
9
+ background: #ffffff;
10
+ font-family: 'Merriweather', Georgia, serif;
11
+ font-size: 12pt;
12
+ color: #111827;
13
+ line-height: 1.6;
14
+
15
+ @media print {
16
+ padding: 30px;
17
+ }
18
+ `;
19
+
20
+ const Header = styled.header`
21
+ margin-bottom: 40px;
22
+ padding-bottom: 20px;
23
+ border-bottom: 1px solid #6b7280;
24
+ text-align: center;
25
+ `;
26
+
27
+ const Name = styled.h1`
28
+ font-size: 28pt;
29
+ font-weight: 700;
30
+ color: #111827;
31
+ margin: 0 0 8px 0;
32
+ letter-spacing: 0.5px;
33
+ `;
34
+
35
+ const Label = styled.div`
36
+ font-size: 14pt;
37
+ color: #1f2937;
38
+ margin-bottom: 16px;
39
+ font-weight: 400;
40
+ font-style: italic;
41
+ `;
42
+
43
+ const StyledContactInfo = styled(ContactInfo)`
44
+ font-size: 11pt;
45
+ color: #1f2937;
46
+ justify-content: center;
47
+
48
+ a {
49
+ font-size: 11pt;
50
+ color: #1f2937;
51
+ text-decoration: underline;
52
+ }
53
+ `;
54
+
55
+ const Summary = styled.p`
56
+ font-size: 12pt;
57
+ line-height: 1.6;
58
+ color: #1f2937;
59
+ margin: 20px 0 0 0;
60
+ text-align: left;
61
+ `;
62
+
63
+ const StyledSectionTitle = styled(SectionTitle)`
64
+ font-size: 16pt;
65
+ font-weight: 700;
66
+ color: #111827;
67
+ margin: 32px 0 16px 0;
68
+ padding-bottom: 6px;
69
+ border-bottom: 2px solid #111827;
70
+ text-transform: uppercase;
71
+ letter-spacing: 1px;
72
+ `;
73
+
74
+ const PublicationItem = styled.div`
75
+ margin-bottom: 20px;
76
+ padding-left: 24px;
77
+ text-indent: -24px;
78
+
79
+ &:last-child {
80
+ margin-bottom: 0;
81
+ }
82
+ `;
83
+
84
+ const PublicationTitle = styled.span`
85
+ font-weight: 700;
86
+ color: #111827;
87
+ `;
88
+
89
+ const PublicationMeta = styled.span`
90
+ color: #1f2937;
91
+ font-style: italic;
92
+ `;
93
+
94
+ const PublicationDate = styled.span`
95
+ color: #6b7280;
96
+ `;
97
+
98
+ const AwardItem = styled.div`
99
+ margin-bottom: 16px;
100
+ padding-left: 20px;
101
+ border-left: 3px solid #9ca3af;
102
+
103
+ &:last-child {
104
+ margin-bottom: 0;
105
+ }
106
+ `;
107
+
108
+ const AwardTitle = styled.h3`
109
+ font-size: 13pt;
110
+ font-weight: 700;
111
+ color: #111827;
112
+ margin: 0 0 4px 0;
113
+ `;
114
+
115
+ const AwardAwarder = styled.div`
116
+ font-size: 11pt;
117
+ color: #1f2937;
118
+ font-style: italic;
119
+ margin-bottom: 4px;
120
+ `;
121
+
122
+ const AwardDate = styled.div`
123
+ font-size: 11pt;
124
+ color: #6b7280;
125
+ margin-bottom: 8px;
126
+ `;
127
+
128
+ const AwardSummary = styled.p`
129
+ margin: 0;
130
+ font-size: 11pt;
131
+ color: #1f2937;
132
+ line-height: 1.5;
133
+ `;
134
+
135
+ const WorkItem = styled.div`
136
+ margin-bottom: 24px;
137
+
138
+ &:last-child {
139
+ margin-bottom: 0;
140
+ }
141
+ `;
142
+
143
+ const WorkHeader = styled.div`
144
+ margin-bottom: 8px;
145
+ `;
146
+
147
+ const Position = styled.h3`
148
+ font-size: 13pt;
149
+ font-weight: 700;
150
+ color: #111827;
151
+ margin: 0 0 4px 0;
152
+ `;
153
+
154
+ const Company = styled.div`
155
+ font-size: 12pt;
156
+ color: #1f2937;
157
+ font-style: italic;
158
+ margin-bottom: 4px;
159
+ `;
160
+
161
+ const DateText = styled.div`
162
+ font-size: 11pt;
163
+ color: #6b7280;
164
+ margin-bottom: 8px;
165
+ `;
166
+
167
+ const WorkSummary = styled.p`
168
+ margin: 8px 0;
169
+ color: #1f2937;
170
+ line-height: 1.5;
171
+ font-size: 11pt;
172
+ `;
173
+
174
+ const Highlights = styled.ul`
175
+ margin: 8px 0 0 0;
176
+ padding-left: 20px;
177
+ list-style-type: disc;
178
+
179
+ li {
180
+ margin: 4px 0;
181
+ color: #1f2937;
182
+ line-height: 1.5;
183
+ font-size: 11pt;
184
+ }
185
+ `;
186
+
187
+ const EducationItem = styled.div`
188
+ margin-bottom: 20px;
189
+
190
+ &:last-child {
191
+ margin-bottom: 0;
192
+ }
193
+ `;
194
+
195
+ const Institution = styled.h3`
196
+ font-size: 13pt;
197
+ font-weight: 700;
198
+ color: #111827;
199
+ margin: 0 0 4px 0;
200
+ `;
201
+
202
+ const Degree = styled.div`
203
+ font-size: 12pt;
204
+ color: #1f2937;
205
+ margin-bottom: 4px;
206
+ `;
207
+
208
+ const EducationDate = styled.div`
209
+ font-size: 11pt;
210
+ color: #6b7280;
211
+ `;
212
+
213
+ const SkillsList = styled.div`
214
+ display: flex;
215
+ flex-direction: column;
216
+ gap: 12px;
217
+ `;
218
+
219
+ const SkillItem = styled.div`
220
+ display: flex;
221
+ gap: 8px;
222
+ `;
223
+
224
+ const SkillName = styled.span`
225
+ font-weight: 700;
226
+ color: #111827;
227
+ font-size: 12pt;
228
+ min-width: 120px;
229
+ `;
230
+
231
+ const SkillKeywords = styled.span`
232
+ color: #1f2937;
233
+ font-size: 11pt;
234
+ `;
235
+
236
+ function Resume({ resume }) {
237
+ const {
238
+ basics = {},
239
+ work = [],
240
+ education = [],
241
+ skills = [],
242
+ projects = [],
243
+ volunteer = [],
244
+ awards = [],
245
+ publications = [],
246
+ languages = [],
247
+ interests = [],
248
+ references = [],
249
+ } = resume;
250
+
251
+ return (
252
+ <Layout>
253
+ <Header>
254
+ <Name>{basics.name}</Name>
255
+ {basics.label && <Label>{basics.label}</Label>}
256
+ <StyledContactInfo basics={basics} />
257
+ {basics.summary && <Summary>{basics.summary}</Summary>}
258
+ </Header>
259
+
260
+ {education?.length > 0 && (
261
+ <Section>
262
+ <StyledSectionTitle>Education</StyledSectionTitle>
263
+ {education.map((edu, index) => (
264
+ <EducationItem key={index}>
265
+ <Institution>{edu.institution}</Institution>
266
+ <Degree>
267
+ {edu.studyType} in {edu.area}
268
+ {edu.score && ` • ${edu.score}`}
269
+ </Degree>
270
+ <EducationDate>
271
+ <DateRange startDate={edu.startDate} endDate={edu.endDate} />
272
+ </EducationDate>
273
+ </EducationItem>
274
+ ))}
275
+ </Section>
276
+ )}
277
+
278
+ {publications?.length > 0 && (
279
+ <Section>
280
+ <StyledSectionTitle>Publications</StyledSectionTitle>
281
+ {publications.map((pub, index) => (
282
+ <PublicationItem key={index}>
283
+ <PublicationTitle>{pub.name}. </PublicationTitle>
284
+ {pub.publisher && (
285
+ <PublicationMeta>{pub.publisher}. </PublicationMeta>
286
+ )}
287
+ {pub.releaseDate && (
288
+ <PublicationDate>({pub.releaseDate})</PublicationDate>
289
+ )}
290
+ {pub.summary && <WorkSummary>{pub.summary}</WorkSummary>}
291
+ </PublicationItem>
292
+ ))}
293
+ </Section>
294
+ )}
295
+
296
+ {awards?.length > 0 && (
297
+ <Section>
298
+ <StyledSectionTitle>Awards & Honors</StyledSectionTitle>
299
+ {awards.map((award, index) => (
300
+ <AwardItem key={index}>
301
+ <AwardTitle>{award.title}</AwardTitle>
302
+ {award.awarder && <AwardAwarder>{award.awarder}</AwardAwarder>}
303
+ {award.date && <AwardDate>{award.date}</AwardDate>}
304
+ {award.summary && <AwardSummary>{award.summary}</AwardSummary>}
305
+ </AwardItem>
306
+ ))}
307
+ </Section>
308
+ )}
309
+
310
+ {work?.length > 0 && (
311
+ <Section>
312
+ <StyledSectionTitle>Professional Experience</StyledSectionTitle>
313
+ {work.map((job, index) => (
314
+ <WorkItem key={index}>
315
+ <WorkHeader>
316
+ <Position>{job.position}</Position>
317
+ {job.name && <Company>{job.name}</Company>}
318
+ <DateText>
319
+ <DateRange startDate={job.startDate} endDate={job.endDate} />
320
+ </DateText>
321
+ </WorkHeader>
322
+ {job.summary && <WorkSummary>{job.summary}</WorkSummary>}
323
+ {job.highlights?.length > 0 && (
324
+ <Highlights>
325
+ {job.highlights.map((highlight, i) => (
326
+ <li key={i}>{highlight}</li>
327
+ ))}
328
+ </Highlights>
329
+ )}
330
+ </WorkItem>
331
+ ))}
332
+ </Section>
333
+ )}
334
+
335
+ {projects?.length > 0 && (
336
+ <Section>
337
+ <StyledSectionTitle>Research Projects</StyledSectionTitle>
338
+ {projects.map((project, index) => (
339
+ <WorkItem key={index}>
340
+ <Position>{project.name}</Position>
341
+ {project.description && (
342
+ <WorkSummary>{project.description}</WorkSummary>
343
+ )}
344
+ {project.highlights?.length > 0 && (
345
+ <Highlights>
346
+ {project.highlights.map((highlight, i) => (
347
+ <li key={i}>{highlight}</li>
348
+ ))}
349
+ </Highlights>
350
+ )}
351
+ </WorkItem>
352
+ ))}
353
+ </Section>
354
+ )}
355
+
356
+ {skills?.length > 0 && (
357
+ <Section>
358
+ <StyledSectionTitle>Skills</StyledSectionTitle>
359
+ <SkillsList>
360
+ {skills.map((skill, index) => (
361
+ <SkillItem key={index}>
362
+ <SkillName>{skill.name}:</SkillName>
363
+ {skill.keywords?.length > 0 && (
364
+ <SkillKeywords>{skill.keywords.join(', ')}</SkillKeywords>
365
+ )}
366
+ </SkillItem>
367
+ ))}
368
+ </SkillsList>
369
+ </Section>
370
+ )}
371
+
372
+ {volunteer?.length > 0 && (
373
+ <Section>
374
+ <StyledSectionTitle>Service & Leadership</StyledSectionTitle>
375
+ {volunteer.map((vol, index) => (
376
+ <WorkItem key={index}>
377
+ <WorkHeader>
378
+ <Position>{vol.position}</Position>
379
+ {vol.organization && <Company>{vol.organization}</Company>}
380
+ {(vol.startDate || vol.endDate) && (
381
+ <DateText>
382
+ <DateRange
383
+ startDate={vol.startDate}
384
+ endDate={vol.endDate}
385
+ />
386
+ </DateText>
387
+ )}
388
+ </WorkHeader>
389
+ {vol.summary && <WorkSummary>{vol.summary}</WorkSummary>}
390
+ {vol.highlights?.length > 0 && (
391
+ <Highlights>
392
+ {vol.highlights.map((highlight, i) => (
393
+ <li key={i}>{highlight}</li>
394
+ ))}
395
+ </Highlights>
396
+ )}
397
+ </WorkItem>
398
+ ))}
399
+ </Section>
400
+ )}
401
+
402
+ {languages?.length > 0 && (
403
+ <Section>
404
+ <StyledSectionTitle>Languages</StyledSectionTitle>
405
+ <SkillsList>
406
+ {languages.map((lang, index) => (
407
+ <SkillItem key={index}>
408
+ <SkillName>{lang.language}:</SkillName>
409
+ {lang.fluency && <SkillKeywords>{lang.fluency}</SkillKeywords>}
410
+ </SkillItem>
411
+ ))}
412
+ </SkillsList>
413
+ </Section>
414
+ )}
415
+
416
+ {interests?.length > 0 && (
417
+ <Section>
418
+ <StyledSectionTitle>Research Interests</StyledSectionTitle>
419
+ <SkillsList>
420
+ {interests.map((interest, index) => (
421
+ <SkillItem key={index}>
422
+ <SkillName>{interest.name}</SkillName>
423
+ {interest.keywords?.length > 0 && (
424
+ <SkillKeywords>{interest.keywords.join(', ')}</SkillKeywords>
425
+ )}
426
+ </SkillItem>
427
+ ))}
428
+ </SkillsList>
429
+ </Section>
430
+ )}
431
+
432
+ {references?.length > 0 && (
433
+ <Section>
434
+ <StyledSectionTitle>References</StyledSectionTitle>
435
+ {references.map((ref, index) => (
436
+ <EducationItem key={index}>
437
+ <Institution>{ref.name}</Institution>
438
+ {ref.reference && <WorkSummary>{ref.reference}</WorkSummary>}
439
+ </EducationItem>
440
+ ))}
441
+ </Section>
442
+ )}
443
+ </Layout>
444
+ );
445
+ }
446
+
447
+ export default Resume;
package/src/index.js ADDED
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { renderToString } from 'react-dom/server';
3
+ import { ServerStyleSheet } from 'styled-components';
4
+ import Resume from './Resume.jsx';
5
+
6
+ export function render(resume) {
7
+ const sheet = new ServerStyleSheet();
8
+
9
+ try {
10
+ const html = renderToString(
11
+ sheet.collectStyles(<Resume resume={resume} />)
12
+ );
13
+ const styles = sheet.getStyleTags();
14
+
15
+ return `<!DOCTYPE html>
16
+ <html lang="en">
17
+ <head>
18
+ <meta charset="UTF-8">
19
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
20
+ <title>${resume.basics?.name || 'Resume'} - Curriculum Vitae</title>
21
+ <link rel="preconnect" href="https://fonts.googleapis.com">
22
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
23
+ <link href="https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
24
+ ${styles}
25
+ <style>
26
+ * {
27
+ box-sizing: border-box;
28
+ margin: 0;
29
+ padding: 0;
30
+ }
31
+ body {
32
+ margin: 0;
33
+ padding: 0;
34
+ background: #f5f5f5;
35
+ }
36
+ @media print {
37
+ body {
38
+ background: white;
39
+ }
40
+ @page {
41
+ margin: 0.75in;
42
+ }
43
+ }
44
+ </style>
45
+ </head>
46
+ <body>
47
+ ${html}
48
+ </body>
49
+ </html>`;
50
+ } finally {
51
+ sheet.seal();
52
+ }
53
+ }