jsonresume-theme-developer-mono 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/index.js ADDED
@@ -0,0 +1,23 @@
1
+ import { renderToString } from 'react-dom/server';
2
+ import { ServerStyleSheet } from 'styled-components';
3
+ import Resume from './src/Resume.jsx';
4
+
5
+ export function render(resume) {
6
+ const sheet = new ServerStyleSheet();
7
+ const html = renderToString(sheet.collectStyles(<Resume resume={resume} />));
8
+ const styles = sheet.getStyleTags();
9
+ const title = (resume.basics && resume.basics.name) || 'Resume';
10
+
11
+ return `<!DOCTYPE html>
12
+ <html lang="en">
13
+ <head>
14
+ <meta charset="utf-8">
15
+ <title>${title}</title>
16
+ <meta name="viewport" content="width=device-width, initial-scale=1">
17
+ <link rel="preconnect" href="https://fonts.googleapis.com">
18
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
19
+ ${styles}
20
+ </head>
21
+ <body>${html}</body>
22
+ </html>`;
23
+ }
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "jsonresume-theme-developer-mono",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "index.js",
6
+ "peerDependencies": {
7
+ "react": "^18.0.0 || ^19.0.0",
8
+ "react-dom": "^18.0.0 || ^19.0.0"
9
+ },
10
+ "dependencies": {
11
+ "styled-components": "^6.1.19",
12
+ "@resume/core": "0.1.0"
13
+ }
14
+ }
package/src/Resume.jsx ADDED
@@ -0,0 +1,595 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import {
4
+ Section,
5
+ SectionTitle,
6
+ DateRange,
7
+ ContactInfo,
8
+ Link,
9
+ } from '@resume/core';
10
+
11
+ const Layout = styled.div`
12
+ max-width: 900px;
13
+ margin: 0 auto;
14
+ padding: 60px 50px;
15
+ background: #ffffff;
16
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',
17
+ sans-serif;
18
+ color: #1f2937;
19
+ line-height: 1.7;
20
+
21
+ @media print {
22
+ padding: 40px;
23
+ }
24
+
25
+ @media (max-width: 640px) {
26
+ padding: 40px 20px;
27
+ }
28
+ `;
29
+
30
+ const Header = styled.header`
31
+ margin-bottom: 48px;
32
+ padding-bottom: 24px;
33
+ border-bottom: 3px solid #2563eb;
34
+ `;
35
+
36
+ const Name = styled.h1`
37
+ font-size: 48px;
38
+ font-weight: 700;
39
+ font-family: 'JetBrains Mono', 'Courier New', monospace;
40
+ color: #111827;
41
+ margin: 0 0 12px 0;
42
+ letter-spacing: -1px;
43
+ `;
44
+
45
+ const Label = styled.p`
46
+ font-size: 18px;
47
+ font-weight: 500;
48
+ font-family: 'JetBrains Mono', monospace;
49
+ color: #2563eb;
50
+ margin: 0 0 20px 0;
51
+ letter-spacing: 0.5px;
52
+ `;
53
+
54
+ const StyledContactInfo = styled(ContactInfo)`
55
+ font-size: 15px;
56
+ color: #6b7280;
57
+ margin-bottom: 20px;
58
+
59
+ a {
60
+ font-size: 15px;
61
+ color: #2563eb;
62
+ text-decoration: none;
63
+ font-family: 'JetBrains Mono', monospace;
64
+
65
+ &:hover {
66
+ text-decoration: underline;
67
+ }
68
+ }
69
+ `;
70
+
71
+ const Summary = styled.p`
72
+ font-size: 16px;
73
+ line-height: 1.8;
74
+ color: #374151;
75
+ margin: 20px 0 0 0;
76
+ max-width: 750px;
77
+ `;
78
+
79
+ const StyledSection = styled(Section)`
80
+ margin-bottom: 48px;
81
+ `;
82
+
83
+ const StyledSectionTitle = styled(SectionTitle)`
84
+ font-size: 20px;
85
+ font-weight: 700;
86
+ font-family: 'JetBrains Mono', monospace;
87
+ color: #111827;
88
+ margin: 0 0 24px 0;
89
+ text-transform: uppercase;
90
+ letter-spacing: 1px;
91
+ padding: 8px 0;
92
+ border-bottom: 2px solid #e5e7eb;
93
+ display: inline-block;
94
+ min-width: 200px;
95
+
96
+ &::before {
97
+ content: '# ';
98
+ color: #2563eb;
99
+ }
100
+ `;
101
+
102
+ const WorkItem = styled.div`
103
+ margin-bottom: 36px;
104
+ padding-left: 20px;
105
+ border-left: 3px solid #e5e7eb;
106
+
107
+ &:last-child {
108
+ margin-bottom: 0;
109
+ }
110
+
111
+ &:hover {
112
+ border-left-color: #2563eb;
113
+ }
114
+ `;
115
+
116
+ const WorkHeader = styled.div`
117
+ margin-bottom: 12px;
118
+ `;
119
+
120
+ const WorkTitle = styled.div`
121
+ display: flex;
122
+ justify-content: space-between;
123
+ align-items: baseline;
124
+ flex-wrap: wrap;
125
+ gap: 12px;
126
+ margin-bottom: 8px;
127
+ `;
128
+
129
+ const Position = styled.h3`
130
+ font-size: 18px;
131
+ font-weight: 600;
132
+ font-family: 'JetBrains Mono', monospace;
133
+ color: #111827;
134
+ margin: 0;
135
+ `;
136
+
137
+ const Company = styled.div`
138
+ font-size: 16px;
139
+ font-weight: 500;
140
+ color: #2563eb;
141
+ margin-top: 4px;
142
+ `;
143
+
144
+ const StyledDateRange = styled(DateRange)`
145
+ font-size: 14px;
146
+ font-family: 'JetBrains Mono', monospace;
147
+ color: #6b7280;
148
+ `;
149
+
150
+ const WorkSummary = styled.p`
151
+ margin: 12px 0;
152
+ color: #4b5563;
153
+ line-height: 1.7;
154
+ font-size: 15px;
155
+ `;
156
+
157
+ const HighlightsList = styled.ul`
158
+ margin: 12px 0 0 0;
159
+ padding-left: 20px;
160
+ list-style: none;
161
+
162
+ li {
163
+ position: relative;
164
+ margin-bottom: 8px;
165
+ padding-left: 0;
166
+ color: #374151;
167
+ line-height: 1.7;
168
+
169
+ &::before {
170
+ content: '→';
171
+ position: absolute;
172
+ left: -20px;
173
+ color: #2563eb;
174
+ font-weight: bold;
175
+ }
176
+ }
177
+ `;
178
+
179
+ const EducationItem = styled.div`
180
+ margin-bottom: 28px;
181
+ padding: 20px;
182
+ background: #f9fafb;
183
+ border-left: 3px solid #2563eb;
184
+ border-radius: 2px;
185
+
186
+ &:last-child {
187
+ margin-bottom: 0;
188
+ }
189
+ `;
190
+
191
+ const EducationHeader = styled.div`
192
+ display: flex;
193
+ justify-content: space-between;
194
+ align-items: baseline;
195
+ flex-wrap: wrap;
196
+ gap: 8px;
197
+ margin-bottom: 8px;
198
+ `;
199
+
200
+ const Degree = styled.h3`
201
+ font-size: 17px;
202
+ font-weight: 600;
203
+ font-family: 'JetBrains Mono', monospace;
204
+ color: #111827;
205
+ margin: 0;
206
+ `;
207
+
208
+ const Institution = styled.div`
209
+ font-size: 15px;
210
+ color: #6b7280;
211
+ margin-top: 4px;
212
+ `;
213
+
214
+ const StudyType = styled.div`
215
+ font-size: 14px;
216
+ color: #2563eb;
217
+ margin-top: 4px;
218
+ `;
219
+
220
+ const SkillsGrid = styled.div`
221
+ display: grid;
222
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
223
+ gap: 16px;
224
+ `;
225
+
226
+ const SkillCard = styled.div`
227
+ padding: 16px;
228
+ background: #f9fafb;
229
+ border: 1px solid #e5e7eb;
230
+ border-radius: 2px;
231
+ transition: all 0.2s ease;
232
+
233
+ &:hover {
234
+ border-color: #2563eb;
235
+ background: #eff6ff;
236
+ }
237
+ `;
238
+
239
+ const SkillName = styled.h4`
240
+ font-size: 15px;
241
+ font-weight: 600;
242
+ font-family: 'JetBrains Mono', monospace;
243
+ color: #111827;
244
+ margin: 0 0 10px 0;
245
+ `;
246
+
247
+ const KeywordList = styled.div`
248
+ font-size: 13px;
249
+ color: #6b7280;
250
+ line-height: 1.6;
251
+ `;
252
+
253
+ const ProjectItem = styled.div`
254
+ margin-bottom: 32px;
255
+ padding-bottom: 32px;
256
+ border-bottom: 1px solid #e5e7eb;
257
+
258
+ &:last-child {
259
+ border-bottom: none;
260
+ padding-bottom: 0;
261
+ margin-bottom: 0;
262
+ }
263
+ `;
264
+
265
+ const ProjectHeader = styled.div`
266
+ margin-bottom: 12px;
267
+ `;
268
+
269
+ const ProjectName = styled.h3`
270
+ font-size: 17px;
271
+ font-weight: 600;
272
+ font-family: 'JetBrains Mono', monospace;
273
+ color: #111827;
274
+ margin: 0 0 8px 0;
275
+ `;
276
+
277
+ const ProjectDescription = styled.p`
278
+ font-size: 15px;
279
+ color: #4b5563;
280
+ line-height: 1.7;
281
+ margin: 0;
282
+ `;
283
+
284
+ const ProjectHighlights = styled.ul`
285
+ margin: 12px 0 0 0;
286
+ padding-left: 20px;
287
+ list-style: none;
288
+
289
+ li {
290
+ position: relative;
291
+ margin-bottom: 6px;
292
+ padding-left: 0;
293
+ color: #4b5563;
294
+ font-size: 14px;
295
+
296
+ &::before {
297
+ content: '•';
298
+ position: absolute;
299
+ left: -20px;
300
+ color: #2563eb;
301
+ }
302
+ }
303
+ `;
304
+
305
+ const SimpleList = styled.div`
306
+ display: grid;
307
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
308
+ gap: 16px;
309
+ `;
310
+
311
+ const SimpleItem = styled.div`
312
+ padding: 16px;
313
+ background: #f9fafb;
314
+ border-left: 2px solid #2563eb;
315
+ border-radius: 2px;
316
+ `;
317
+
318
+ const ItemTitle = styled.h4`
319
+ font-size: 15px;
320
+ font-weight: 600;
321
+ font-family: 'JetBrains Mono', monospace;
322
+ color: #111827;
323
+ margin: 0 0 8px 0;
324
+ `;
325
+
326
+ const ItemMeta = styled.div`
327
+ font-size: 13px;
328
+ color: #6b7280;
329
+ margin-bottom: 6px;
330
+ `;
331
+
332
+ const ItemDescription = styled.p`
333
+ font-size: 14px;
334
+ color: #4b5563;
335
+ margin: 8px 0 0 0;
336
+ line-height: 1.6;
337
+ `;
338
+
339
+ const CodeBlock = styled.pre`
340
+ font-family: 'JetBrains Mono', monospace;
341
+ font-size: 13px;
342
+ color: #1f2937;
343
+ background: #f9fafb;
344
+ padding: 2px 6px;
345
+ border-radius: 2px;
346
+ display: inline;
347
+ `;
348
+
349
+ function Resume({ resume }) {
350
+ const {
351
+ basics = {},
352
+ work = [],
353
+ education = [],
354
+ skills = [],
355
+ projects = [],
356
+ volunteer = [],
357
+ awards = [],
358
+ publications = [],
359
+ languages = [],
360
+ interests = [],
361
+ references = [],
362
+ } = resume;
363
+
364
+ return (
365
+ <Layout>
366
+ <Header>
367
+ <Name>{basics.name}</Name>
368
+ {basics.label && <Label>{basics.label}</Label>}
369
+ <StyledContactInfo basics={basics} />
370
+ {basics.summary && <Summary>{basics.summary}</Summary>}
371
+ </Header>
372
+
373
+ {work && work.length > 0 && (
374
+ <StyledSection>
375
+ <StyledSectionTitle>Experience</StyledSectionTitle>
376
+ {work.map((job, index) => (
377
+ <WorkItem key={index}>
378
+ <WorkHeader>
379
+ <WorkTitle>
380
+ <div>
381
+ <Position>{job.position}</Position>
382
+ <Company>{job.name}</Company>
383
+ </div>
384
+ <StyledDateRange
385
+ startDate={job.startDate}
386
+ endDate={job.endDate}
387
+ />
388
+ </WorkTitle>
389
+ </WorkHeader>
390
+ {job.summary && <WorkSummary>{job.summary}</WorkSummary>}
391
+ {job.highlights && job.highlights.length > 0 && (
392
+ <HighlightsList>
393
+ {job.highlights.map((highlight, i) => (
394
+ <li
395
+ key={i}
396
+ dangerouslySetInnerHTML={{ __html: highlight }}
397
+ />
398
+ ))}
399
+ </HighlightsList>
400
+ )}
401
+ </WorkItem>
402
+ ))}
403
+ </StyledSection>
404
+ )}
405
+
406
+ {skills && skills.length > 0 && (
407
+ <StyledSection>
408
+ <StyledSectionTitle>Skills</StyledSectionTitle>
409
+ <SkillsGrid>
410
+ {skills.map((skill, index) => (
411
+ <SkillCard key={index}>
412
+ <SkillName>{skill.name}</SkillName>
413
+ {skill.keywords && skill.keywords.length > 0 && (
414
+ <KeywordList>{skill.keywords.join(' • ')}</KeywordList>
415
+ )}
416
+ </SkillCard>
417
+ ))}
418
+ </SkillsGrid>
419
+ </StyledSection>
420
+ )}
421
+
422
+ {education && education.length > 0 && (
423
+ <StyledSection>
424
+ <StyledSectionTitle>Education</StyledSectionTitle>
425
+ {education.map((edu, index) => (
426
+ <EducationItem key={index}>
427
+ <EducationHeader>
428
+ <div>
429
+ <Degree>{edu.area}</Degree>
430
+ {edu.studyType && <StudyType>{edu.studyType}</StudyType>}
431
+ <Institution>{edu.institution}</Institution>
432
+ </div>
433
+ <StyledDateRange
434
+ startDate={edu.startDate}
435
+ endDate={edu.endDate}
436
+ />
437
+ </EducationHeader>
438
+ {edu.score && <ItemMeta>GPA: {edu.score}</ItemMeta>}
439
+ {edu.courses && edu.courses.length > 0 && (
440
+ <ItemDescription>{edu.courses.join(', ')}</ItemDescription>
441
+ )}
442
+ </EducationItem>
443
+ ))}
444
+ </StyledSection>
445
+ )}
446
+
447
+ {projects && projects.length > 0 && (
448
+ <StyledSection>
449
+ <StyledSectionTitle>Projects</StyledSectionTitle>
450
+ {projects.map((project, index) => (
451
+ <ProjectItem key={index}>
452
+ <ProjectHeader>
453
+ <ProjectName>
454
+ {project.url ? (
455
+ <Link href={project.url}>{project.name}</Link>
456
+ ) : (
457
+ project.name
458
+ )}
459
+ </ProjectName>
460
+ {project.description && (
461
+ <ProjectDescription>{project.description}</ProjectDescription>
462
+ )}
463
+ </ProjectHeader>
464
+ {project.highlights && project.highlights.length > 0 && (
465
+ <ProjectHighlights>
466
+ {project.highlights.map((highlight, i) => (
467
+ <li key={i}>{highlight}</li>
468
+ ))}
469
+ </ProjectHighlights>
470
+ )}
471
+ </ProjectItem>
472
+ ))}
473
+ </StyledSection>
474
+ )}
475
+
476
+ {volunteer && volunteer.length > 0 && (
477
+ <StyledSection>
478
+ <StyledSectionTitle>Volunteer</StyledSectionTitle>
479
+ <SimpleList>
480
+ {volunteer.map((vol, index) => (
481
+ <SimpleItem key={index}>
482
+ <ItemTitle>{vol.position}</ItemTitle>
483
+ <ItemMeta>
484
+ {vol.organization}
485
+ {vol.startDate && (
486
+ <>
487
+ {' • '}
488
+ <DateRange
489
+ startDate={vol.startDate}
490
+ endDate={vol.endDate}
491
+ />
492
+ </>
493
+ )}
494
+ </ItemMeta>
495
+ {vol.summary && (
496
+ <ItemDescription>{vol.summary}</ItemDescription>
497
+ )}
498
+ </SimpleItem>
499
+ ))}
500
+ </SimpleList>
501
+ </StyledSection>
502
+ )}
503
+
504
+ {awards && awards.length > 0 && (
505
+ <StyledSection>
506
+ <StyledSectionTitle>Awards</StyledSectionTitle>
507
+ <SimpleList>
508
+ {awards.map((award, index) => (
509
+ <SimpleItem key={index}>
510
+ <ItemTitle>{award.title}</ItemTitle>
511
+ <ItemMeta>
512
+ {award.awarder}
513
+ {award.date && <> • {award.date}</>}
514
+ </ItemMeta>
515
+ {award.summary && (
516
+ <ItemDescription>{award.summary}</ItemDescription>
517
+ )}
518
+ </SimpleItem>
519
+ ))}
520
+ </SimpleList>
521
+ </StyledSection>
522
+ )}
523
+
524
+ {publications && publications.length > 0 && (
525
+ <StyledSection>
526
+ <StyledSectionTitle>Publications</StyledSectionTitle>
527
+ {publications.map((pub, index) => (
528
+ <ProjectItem key={index}>
529
+ <ProjectHeader>
530
+ <ProjectName>
531
+ {pub.url ? <Link href={pub.url}>{pub.name}</Link> : pub.name}
532
+ </ProjectName>
533
+ <ItemMeta>
534
+ {pub.publisher}
535
+ {pub.releaseDate && <> • {pub.releaseDate}</>}
536
+ </ItemMeta>
537
+ </ProjectHeader>
538
+ {pub.summary && (
539
+ <ProjectDescription>{pub.summary}</ProjectDescription>
540
+ )}
541
+ </ProjectItem>
542
+ ))}
543
+ </StyledSection>
544
+ )}
545
+
546
+ {languages && languages.length > 0 && (
547
+ <StyledSection>
548
+ <StyledSectionTitle>Languages</StyledSectionTitle>
549
+ <SimpleList>
550
+ {languages.map((lang, index) => (
551
+ <SimpleItem key={index}>
552
+ <ItemTitle>{lang.language}</ItemTitle>
553
+ {lang.fluency && <ItemMeta>{lang.fluency}</ItemMeta>}
554
+ </SimpleItem>
555
+ ))}
556
+ </SimpleList>
557
+ </StyledSection>
558
+ )}
559
+
560
+ {interests && interests.length > 0 && (
561
+ <StyledSection>
562
+ <StyledSectionTitle>Interests</StyledSectionTitle>
563
+ <SimpleList>
564
+ {interests.map((interest, index) => (
565
+ <SimpleItem key={index}>
566
+ <ItemTitle>{interest.name}</ItemTitle>
567
+ {interest.keywords && interest.keywords.length > 0 && (
568
+ <ItemDescription>
569
+ {interest.keywords.join(', ')}
570
+ </ItemDescription>
571
+ )}
572
+ </SimpleItem>
573
+ ))}
574
+ </SimpleList>
575
+ </StyledSection>
576
+ )}
577
+
578
+ {references && references.length > 0 && (
579
+ <StyledSection>
580
+ <StyledSectionTitle>References</StyledSectionTitle>
581
+ {references.map((ref, index) => (
582
+ <ProjectItem key={index}>
583
+ <ItemTitle>{ref.name}</ItemTitle>
584
+ {ref.reference && (
585
+ <ItemDescription>{ref.reference}</ItemDescription>
586
+ )}
587
+ </ProjectItem>
588
+ ))}
589
+ </StyledSection>
590
+ )}
591
+ </Layout>
592
+ );
593
+ }
594
+
595
+ 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
+ }