jsonresume-theme-developer-mono 1.0.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,25 @@
1
+ {
2
+ "name": "jsonresume-theme-developer-mono",
3
+ "version": "1.0.0",
4
+ "description": "Technical, efficient, minimalist theme with monospace accents for developers",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "jsonresume",
10
+ "theme",
11
+ "developer",
12
+ "monospace",
13
+ "technical",
14
+ "ats",
15
+ "minimalist"
16
+ ],
17
+ "peerDependencies": {
18
+ "react": "^18.0.0 || ^19.0.0",
19
+ "react-dom": "^18.0.0 || ^19.0.0"
20
+ },
21
+ "dependencies": {
22
+ "styled-components": "^6.1.19",
23
+ "@resume/core": "0.1.0"
24
+ }
25
+ }
package/src/Resume.jsx ADDED
@@ -0,0 +1,455 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { Section, SectionTitle, DateRange, ContactInfo } from '@resume/core';
4
+
5
+ // Developer Mono Theme - Technical, efficient, minimalist
6
+ // Monospace headers with sans-serif body, code-style aesthetics
7
+ // Color: #2563eb (developer blue)
8
+
9
+ const Layout = styled.div`
10
+ max-width: 850px;
11
+ margin: 0 auto;
12
+ padding: 60px 40px;
13
+ background: white;
14
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
15
+ color: #1f2937;
16
+
17
+ @media print {
18
+ padding: 40px;
19
+ }
20
+ `;
21
+
22
+ const Header = styled.header`
23
+ margin-bottom: 48px;
24
+ padding-bottom: 20px;
25
+ border-bottom: 2px solid #2563eb;
26
+ `;
27
+
28
+ const Name = styled.h1`
29
+ font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', 'Courier New',
30
+ monospace;
31
+ font-size: 32px;
32
+ font-weight: 600;
33
+ color: #111827;
34
+ margin: 0 0 8px 0;
35
+ letter-spacing: -0.01em;
36
+
37
+ &::before {
38
+ content: '// ';
39
+ color: #2563eb;
40
+ }
41
+ `;
42
+
43
+ const Label = styled.div`
44
+ font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', 'Courier New',
45
+ monospace;
46
+ font-size: 14px;
47
+ color: #6b7280;
48
+ margin-bottom: 16px;
49
+ font-weight: 500;
50
+
51
+ &::before {
52
+ content: '/* ';
53
+ color: #94a3b8;
54
+ }
55
+
56
+ &::after {
57
+ content: ' */';
58
+ color: #94a3b8;
59
+ }
60
+ `;
61
+
62
+ const StyledContactInfo = styled(ContactInfo)`
63
+ font-size: 13px;
64
+
65
+ a {
66
+ font-size: 13px;
67
+ color: #2563eb;
68
+ text-decoration: none;
69
+
70
+ &:hover {
71
+ text-decoration: underline;
72
+ }
73
+ }
74
+ `;
75
+
76
+ const Summary = styled.p`
77
+ font-size: 14px;
78
+ line-height: 1.7;
79
+ color: #4b5563;
80
+ margin: 16px 0 0 0;
81
+ `;
82
+
83
+ const StyledSectionTitle = styled(SectionTitle)`
84
+ font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', 'Courier New',
85
+ monospace;
86
+ font-size: 13px;
87
+ font-weight: 700;
88
+ color: #64748b;
89
+ margin: 40px 0 20px 0;
90
+ padding-bottom: 8px;
91
+ border-bottom: 1px solid #e5e7eb;
92
+ letter-spacing: 0.05em;
93
+ text-transform: uppercase;
94
+
95
+ &::before {
96
+ content: '// ';
97
+ color: #94a3b8;
98
+ }
99
+ `;
100
+
101
+ const WorkItem = styled.div`
102
+ margin-bottom: 28px;
103
+
104
+ &:last-child {
105
+ margin-bottom: 0;
106
+ }
107
+ `;
108
+
109
+ const WorkHeader = styled.div`
110
+ display: flex;
111
+ justify-content: space-between;
112
+ align-items: baseline;
113
+ margin-bottom: 6px;
114
+ gap: 16px;
115
+
116
+ @media (max-width: 640px) {
117
+ flex-direction: column;
118
+ align-items: flex-start;
119
+ gap: 4px;
120
+ }
121
+ `;
122
+
123
+ const Position = styled.h3`
124
+ font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', 'Courier New',
125
+ monospace;
126
+ font-size: 15px;
127
+ font-weight: 600;
128
+ color: #111827;
129
+ margin: 0;
130
+ letter-spacing: -0.01em;
131
+ `;
132
+
133
+ const Company = styled.div`
134
+ font-size: 14px;
135
+ color: #4b5563;
136
+ font-weight: 500;
137
+ margin-top: 4px;
138
+ `;
139
+
140
+ const DateText = styled.div`
141
+ font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', 'Courier New',
142
+ monospace;
143
+ font-size: 12px;
144
+ color: #6b7280;
145
+ font-weight: 400;
146
+ white-space: nowrap;
147
+ `;
148
+
149
+ const WorkSummary = styled.p`
150
+ margin: 10px 0;
151
+ color: #4b5563;
152
+ line-height: 1.6;
153
+ font-size: 14px;
154
+ `;
155
+
156
+ const Highlights = styled.ul`
157
+ margin: 10px 0 0 0;
158
+ padding-left: 18px;
159
+ list-style-type: none;
160
+
161
+ li {
162
+ margin: 6px 0;
163
+ color: #4b5563;
164
+ line-height: 1.6;
165
+ font-size: 13px;
166
+ padding-left: 4px;
167
+ position: relative;
168
+
169
+ &:before {
170
+ content: '>';
171
+ position: absolute;
172
+ left: -14px;
173
+ color: #2563eb;
174
+ font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas',
175
+ 'Courier New', monospace;
176
+ font-weight: 600;
177
+ }
178
+ }
179
+ `;
180
+
181
+ const EducationItem = styled.div`
182
+ margin-bottom: 20px;
183
+
184
+ &:last-child {
185
+ margin-bottom: 0;
186
+ }
187
+ `;
188
+
189
+ const Institution = styled.h3`
190
+ font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', 'Courier New',
191
+ monospace;
192
+ font-size: 14px;
193
+ font-weight: 600;
194
+ color: #111827;
195
+ margin: 0 0 6px 0;
196
+ letter-spacing: -0.01em;
197
+ `;
198
+
199
+ const Degree = styled.div`
200
+ font-size: 13px;
201
+ color: #4b5563;
202
+ margin-bottom: 4px;
203
+ `;
204
+
205
+ const EducationDate = styled.div`
206
+ font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', 'Courier New',
207
+ monospace;
208
+ font-size: 12px;
209
+ color: #6b7280;
210
+ `;
211
+
212
+ const SkillsGrid = styled.div`
213
+ display: grid;
214
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
215
+ gap: 12px;
216
+ `;
217
+
218
+ const SkillCategory = styled.div`
219
+ padding: 12px;
220
+ background: #f8fafc;
221
+ border-radius: 2px;
222
+ border-left: 3px solid #2563eb;
223
+ border: 1px solid #e2e8f0;
224
+ `;
225
+
226
+ const SkillName = styled.h4`
227
+ font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', 'Courier New',
228
+ monospace;
229
+ font-size: 12px;
230
+ font-weight: 600;
231
+ color: #2563eb;
232
+ margin: 0 0 6px 0;
233
+ letter-spacing: 0.01em;
234
+ `;
235
+
236
+ const SkillTags = styled.div`
237
+ font-size: 12px;
238
+ color: #6b7280;
239
+ line-height: 1.5;
240
+ `;
241
+
242
+ function Resume({ resume }) {
243
+ const {
244
+ basics = {},
245
+ work = [],
246
+ education = [],
247
+ skills = [],
248
+ projects = [],
249
+ volunteer = [],
250
+ awards = [],
251
+ publications = [],
252
+ languages = [],
253
+ interests = [],
254
+ references = [],
255
+ } = resume;
256
+
257
+ return (
258
+ <Layout>
259
+ <Header>
260
+ <Name>{basics.name}</Name>
261
+ {basics.label && <Label>{basics.label}</Label>}
262
+ <StyledContactInfo basics={basics} />
263
+ {basics.summary && <Summary>{basics.summary}</Summary>}
264
+ </Header>
265
+
266
+ {work?.length > 0 && (
267
+ <Section>
268
+ <StyledSectionTitle>Experience</StyledSectionTitle>
269
+ {work.map((job, index) => (
270
+ <WorkItem key={index}>
271
+ <WorkHeader>
272
+ <div>
273
+ <Position>{job.position}</Position>
274
+ {job.name && <Company>{job.name}</Company>}
275
+ </div>
276
+ <DateText>
277
+ <DateRange startDate={job.startDate} endDate={job.endDate} />
278
+ </DateText>
279
+ </WorkHeader>
280
+ {job.summary && <WorkSummary>{job.summary}</WorkSummary>}
281
+ {job.highlights?.length > 0 && (
282
+ <Highlights>
283
+ {job.highlights.map((highlight, i) => (
284
+ <li key={i}>{highlight}</li>
285
+ ))}
286
+ </Highlights>
287
+ )}
288
+ </WorkItem>
289
+ ))}
290
+ </Section>
291
+ )}
292
+
293
+ {skills?.length > 0 && (
294
+ <Section>
295
+ <StyledSectionTitle>Skills</StyledSectionTitle>
296
+ <SkillsGrid>
297
+ {skills.map((skill, index) => (
298
+ <SkillCategory key={index}>
299
+ <SkillName>{skill.name}</SkillName>
300
+ {skill.keywords?.length > 0 && (
301
+ <SkillTags>{skill.keywords.join(', ')}</SkillTags>
302
+ )}
303
+ </SkillCategory>
304
+ ))}
305
+ </SkillsGrid>
306
+ </Section>
307
+ )}
308
+
309
+ {education?.length > 0 && (
310
+ <Section>
311
+ <StyledSectionTitle>Education</StyledSectionTitle>
312
+ {education.map((edu, index) => (
313
+ <EducationItem key={index}>
314
+ <Institution>{edu.institution}</Institution>
315
+ <Degree>
316
+ {edu.studyType} in {edu.area}
317
+ {edu.score && ` • ${edu.score}`}
318
+ </Degree>
319
+ <EducationDate>
320
+ <DateRange startDate={edu.startDate} endDate={edu.endDate} />
321
+ </EducationDate>
322
+ </EducationItem>
323
+ ))}
324
+ </Section>
325
+ )}
326
+
327
+ {projects?.length > 0 && (
328
+ <Section>
329
+ <StyledSectionTitle>Projects</StyledSectionTitle>
330
+ {projects.map((project, index) => (
331
+ <WorkItem key={index}>
332
+ <Position>{project.name}</Position>
333
+ {project.description && (
334
+ <WorkSummary>{project.description}</WorkSummary>
335
+ )}
336
+ {project.highlights?.length > 0 && (
337
+ <Highlights>
338
+ {project.highlights.map((highlight, i) => (
339
+ <li key={i}>{highlight}</li>
340
+ ))}
341
+ </Highlights>
342
+ )}
343
+ </WorkItem>
344
+ ))}
345
+ </Section>
346
+ )}
347
+
348
+ {volunteer?.length > 0 && (
349
+ <Section>
350
+ <StyledSectionTitle>Volunteer</StyledSectionTitle>
351
+ {volunteer.map((vol, index) => (
352
+ <WorkItem key={index}>
353
+ <WorkHeader>
354
+ <div>
355
+ <Position>{vol.position}</Position>
356
+ {vol.organization && <Company>{vol.organization}</Company>}
357
+ </div>
358
+ {(vol.startDate || vol.endDate) && (
359
+ <DateText>
360
+ <DateRange
361
+ startDate={vol.startDate}
362
+ endDate={vol.endDate}
363
+ />
364
+ </DateText>
365
+ )}
366
+ </WorkHeader>
367
+ {vol.summary && <WorkSummary>{vol.summary}</WorkSummary>}
368
+ {vol.highlights?.length > 0 && (
369
+ <Highlights>
370
+ {vol.highlights.map((highlight, i) => (
371
+ <li key={i}>{highlight}</li>
372
+ ))}
373
+ </Highlights>
374
+ )}
375
+ </WorkItem>
376
+ ))}
377
+ </Section>
378
+ )}
379
+
380
+ {awards?.length > 0 && (
381
+ <Section>
382
+ <StyledSectionTitle>Awards</StyledSectionTitle>
383
+ {awards.map((award, index) => (
384
+ <EducationItem key={index}>
385
+ <Institution>{award.title}</Institution>
386
+ {award.awarder && <Degree>Awarded by {award.awarder}</Degree>}
387
+ {award.date && <EducationDate>{award.date}</EducationDate>}
388
+ {award.summary && <WorkSummary>{award.summary}</WorkSummary>}
389
+ </EducationItem>
390
+ ))}
391
+ </Section>
392
+ )}
393
+
394
+ {publications?.length > 0 && (
395
+ <Section>
396
+ <StyledSectionTitle>Publications</StyledSectionTitle>
397
+ {publications.map((pub, index) => (
398
+ <EducationItem key={index}>
399
+ <Institution>{pub.name}</Institution>
400
+ {pub.publisher && <Degree>Published by {pub.publisher}</Degree>}
401
+ {pub.releaseDate && (
402
+ <EducationDate>{pub.releaseDate}</EducationDate>
403
+ )}
404
+ {pub.summary && <WorkSummary>{pub.summary}</WorkSummary>}
405
+ </EducationItem>
406
+ ))}
407
+ </Section>
408
+ )}
409
+
410
+ {languages?.length > 0 && (
411
+ <Section>
412
+ <StyledSectionTitle>Languages</StyledSectionTitle>
413
+ <SkillsGrid>
414
+ {languages.map((lang, index) => (
415
+ <SkillCategory key={index}>
416
+ <SkillName>{lang.language}</SkillName>
417
+ {lang.fluency && <SkillTags>{lang.fluency}</SkillTags>}
418
+ </SkillCategory>
419
+ ))}
420
+ </SkillsGrid>
421
+ </Section>
422
+ )}
423
+
424
+ {interests?.length > 0 && (
425
+ <Section>
426
+ <StyledSectionTitle>Interests</StyledSectionTitle>
427
+ <SkillsGrid>
428
+ {interests.map((interest, index) => (
429
+ <SkillCategory key={index}>
430
+ <SkillName>{interest.name}</SkillName>
431
+ {interest.keywords?.length > 0 && (
432
+ <SkillTags>{interest.keywords.join(', ')}</SkillTags>
433
+ )}
434
+ </SkillCategory>
435
+ ))}
436
+ </SkillsGrid>
437
+ </Section>
438
+ )}
439
+
440
+ {references?.length > 0 && (
441
+ <Section>
442
+ <StyledSectionTitle>References</StyledSectionTitle>
443
+ {references.map((ref, index) => (
444
+ <EducationItem key={index}>
445
+ <Institution>{ref.name}</Institution>
446
+ {ref.reference && <WorkSummary>{ref.reference}</WorkSummary>}
447
+ </EducationItem>
448
+ ))}
449
+ </Section>
450
+ )}
451
+ </Layout>
452
+ );
453
+ }
454
+
455
+ 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'} - Resume</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=Inter:wght@400;500;600&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: #ffffff;
35
+ }
36
+ @media print {
37
+ body {
38
+ background: white;
39
+ }
40
+ @page {
41
+ margin: 0.5in;
42
+ }
43
+ }
44
+ </style>
45
+ </head>
46
+ <body>
47
+ ${html}
48
+ </body>
49
+ </html>`;
50
+ } finally {
51
+ sheet.seal();
52
+ }
53
+ }