jsonresume-theme-community-garden 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.
Files changed (3) hide show
  1. package/index.js +24 -0
  2. package/package.json +15 -0
  3. package/src/Resume.jsx +627 -0
package/index.js ADDED
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { renderToString } from 'react-dom/server';
3
+ import { ServerStyleSheet } from 'styled-components';
4
+ import Resume from './src/Resume.jsx';
5
+
6
+ export function render(resume) {
7
+ const sheet = new ServerStyleSheet();
8
+ const html = renderToString(sheet.collectStyles(<Resume resume={resume} />));
9
+ const styles = sheet.getStyleTags();
10
+ const title = (resume.basics && resume.basics.name) || 'Resume';
11
+
12
+ return `<!DOCTYPE html>
13
+ <html lang="en">
14
+ <head>
15
+ <meta charset="utf-8">
16
+ <title>${title}</title>
17
+ <meta name="viewport" content="width=device-width, initial-scale=1">
18
+ <link rel="preconnect" href="https://fonts.googleapis.com">
19
+ <link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
20
+ ${styles}
21
+ </head>
22
+ <body>${html}</body>
23
+ </html>`;
24
+ }
package/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "jsonresume-theme-community-garden",
3
+ "version": "0.1.0",
4
+ "description": "Beautiful, warm theme celebrating education and community service. Sky blue and garden green colors with prominent certifications and volunteer sections.",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "peerDependencies": {
8
+ "react": "^18.0.0 || ^19.0.0",
9
+ "react-dom": "^18.0.0 || ^19.0.0"
10
+ },
11
+ "dependencies": {
12
+ "styled-components": "^6.1.19",
13
+ "@resume/core": "0.1.0"
14
+ }
15
+ }
package/src/Resume.jsx ADDED
@@ -0,0 +1,627 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import {
4
+ Section,
5
+ SectionTitle,
6
+ DateRange,
7
+ ContactInfo,
8
+ Link,
9
+ safeUrl,
10
+ } from '@resume/core';
11
+
12
+ const Layout = styled.div`
13
+ max-width: 850px;
14
+ margin: 0 auto;
15
+ padding: 50px 45px;
16
+ background: #ffffff;
17
+ font-family: 'Quicksand', 'Nunito', -apple-system, BlinkMacSystemFont,
18
+ sans-serif;
19
+ color: #2d3748;
20
+ line-height: 1.7;
21
+ font-size: 15.5px;
22
+
23
+ @media print {
24
+ padding: 30px;
25
+ }
26
+ `;
27
+
28
+ const Header = styled.header`
29
+ text-align: center;
30
+ margin-bottom: 48px;
31
+ padding: 36px;
32
+ background: linear-gradient(135deg, #e0f2fe 0%, #d1fae5 100%);
33
+ border-radius: 20px;
34
+ border: 3px solid #38bdf8;
35
+ box-shadow: 0 4px 20px rgba(56, 189, 248, 0.15);
36
+ `;
37
+
38
+ const Name = styled.h1`
39
+ font-size: 36px;
40
+ font-weight: 700;
41
+ margin: 0 0 12px 0;
42
+ color: #0c4a6e;
43
+ letter-spacing: 0.5px;
44
+ `;
45
+
46
+ const Label = styled.div`
47
+ font-size: 19px;
48
+ color: #047857;
49
+ font-weight: 600;
50
+ margin-bottom: 20px;
51
+ font-style: italic;
52
+ `;
53
+
54
+ const ContactWrapper = styled.div`
55
+ display: flex;
56
+ justify-content: center;
57
+ flex-wrap: wrap;
58
+ gap: 20px;
59
+ font-size: 14.5px;
60
+ color: #475569;
61
+ margin-top: 16px;
62
+ `;
63
+
64
+ const SummarySection = styled(Section)`
65
+ margin-bottom: 40px;
66
+ padding: 28px;
67
+ background: #f0fdfa;
68
+ border-left: 5px solid #14b8a6;
69
+ border-radius: 12px;
70
+ `;
71
+
72
+ const SummaryText = styled.p`
73
+ margin: 0;
74
+ font-size: 16px;
75
+ line-height: 1.8;
76
+ color: #1e40af;
77
+ font-weight: 500;
78
+ text-align: center;
79
+ font-style: italic;
80
+ `;
81
+
82
+ const MainSection = styled(Section)`
83
+ margin-bottom: 44px;
84
+ `;
85
+
86
+ const MainSectionTitle = styled(SectionTitle)`
87
+ font-size: 24px;
88
+ font-weight: 700;
89
+ color: #0c4a6e;
90
+ margin: 0 0 24px 0;
91
+ padding-bottom: 12px;
92
+ border-bottom: 4px solid #38bdf8;
93
+ display: inline-block;
94
+ min-width: 200px;
95
+ `;
96
+
97
+ const CertificationsGrid = styled.div`
98
+ display: grid;
99
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
100
+ gap: 20px;
101
+ margin-bottom: 12px;
102
+ `;
103
+
104
+ const CertCard = styled.div`
105
+ padding: 20px;
106
+ background: linear-gradient(135deg, #eff6ff 0%, #ecfdf5 100%);
107
+ border: 2px solid #7dd3fc;
108
+ border-radius: 16px;
109
+ box-shadow: 0 2px 8px rgba(56, 189, 248, 0.1);
110
+ transition: transform 0.2s, box-shadow 0.2s;
111
+
112
+ &:hover {
113
+ transform: translateY(-2px);
114
+ box-shadow: 0 4px 16px rgba(56, 189, 248, 0.2);
115
+ }
116
+ `;
117
+
118
+ const CertName = styled.h3`
119
+ font-size: 17px;
120
+ font-weight: 700;
121
+ margin: 0 0 8px 0;
122
+ color: #0c4a6e;
123
+ `;
124
+
125
+ const CertIssuer = styled.div`
126
+ font-size: 14px;
127
+ color: #059669;
128
+ font-weight: 600;
129
+ margin-bottom: 6px;
130
+ `;
131
+
132
+ const CertDate = styled.div`
133
+ font-size: 13px;
134
+ color: #64748b;
135
+ font-style: italic;
136
+ `;
137
+
138
+ const WorkItem = styled.div`
139
+ margin-bottom: 36px;
140
+ padding: 24px;
141
+ background: #fefce8;
142
+ border-left: 5px solid #84cc16;
143
+ border-radius: 12px;
144
+
145
+ &:last-child {
146
+ margin-bottom: 0;
147
+ }
148
+ `;
149
+
150
+ const WorkHeader = styled.div`
151
+ display: flex;
152
+ justify-content: space-between;
153
+ align-items: baseline;
154
+ margin-bottom: 12px;
155
+ flex-wrap: wrap;
156
+ gap: 10px;
157
+ `;
158
+
159
+ const WorkTitle = styled.h3`
160
+ font-size: 19px;
161
+ font-weight: 700;
162
+ margin: 0;
163
+ color: #0c4a6e;
164
+ `;
165
+
166
+ const WorkMeta = styled.div`
167
+ font-size: 14px;
168
+ color: #65a30d;
169
+ font-weight: 600;
170
+ `;
171
+
172
+ const WorkCompany = styled.div`
173
+ font-size: 16px;
174
+ color: #047857;
175
+ font-weight: 600;
176
+ margin-bottom: 12px;
177
+ `;
178
+
179
+ const WorkDescription = styled.p`
180
+ font-size: 15.5px;
181
+ line-height: 1.7;
182
+ margin: 14px 0 0 0;
183
+ color: #374151;
184
+ `;
185
+
186
+ const WorkHighlights = styled.ul`
187
+ margin: 14px 0 0 0;
188
+ padding-left: 24px;
189
+ list-style: none;
190
+
191
+ li {
192
+ margin-bottom: 10px;
193
+ font-size: 15.5px;
194
+ line-height: 1.7;
195
+ color: #374151;
196
+ position: relative;
197
+ padding-left: 8px;
198
+
199
+ &::before {
200
+ content: '🌱';
201
+ position: absolute;
202
+ left: -24px;
203
+ font-size: 16px;
204
+ }
205
+ }
206
+ `;
207
+
208
+ const VolunteerItem = styled.div`
209
+ margin-bottom: 32px;
210
+ padding: 24px;
211
+ background: linear-gradient(135deg, #f0fdf4 0%, #dbeafe 100%);
212
+ border: 3px solid #10b981;
213
+ border-radius: 16px;
214
+ box-shadow: 0 2px 12px rgba(16, 185, 129, 0.1);
215
+
216
+ &:last-child {
217
+ margin-bottom: 0;
218
+ }
219
+ `;
220
+
221
+ const SkillsGrid = styled.div`
222
+ display: grid;
223
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
224
+ gap: 18px;
225
+ `;
226
+
227
+ const SkillCard = styled.div`
228
+ padding: 18px;
229
+ background: #f0f9ff;
230
+ border-left: 4px solid #0ea5e9;
231
+ border-radius: 10px;
232
+ `;
233
+
234
+ const SkillName = styled.h4`
235
+ font-size: 16px;
236
+ font-weight: 700;
237
+ margin: 0 0 10px 0;
238
+ color: #0c4a6e;
239
+ `;
240
+
241
+ const SkillKeywords = styled.div`
242
+ display: flex;
243
+ flex-wrap: wrap;
244
+ gap: 8px;
245
+ `;
246
+
247
+ const SkillBadge = styled.span`
248
+ display: inline-block;
249
+ padding: 6px 14px;
250
+ background: #bfdbfe;
251
+ color: #1e40af;
252
+ border-radius: 20px;
253
+ font-size: 13px;
254
+ font-weight: 600;
255
+ `;
256
+
257
+ const EducationItem = styled.div`
258
+ margin-bottom: 28px;
259
+ padding: 22px;
260
+ background: #fef3c7;
261
+ border-left: 5px solid #f59e0b;
262
+ border-radius: 12px;
263
+
264
+ &:last-child {
265
+ margin-bottom: 0;
266
+ }
267
+ `;
268
+
269
+ const AwardsList = styled.div`
270
+ display: grid;
271
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
272
+ gap: 18px;
273
+ `;
274
+
275
+ const AwardCard = styled.div`
276
+ padding: 18px;
277
+ background: linear-gradient(135deg, #fef3c7 0%, #fce7f3 100%);
278
+ border: 2px solid #fbbf24;
279
+ border-radius: 14px;
280
+ box-shadow: 0 2px 8px rgba(251, 191, 36, 0.1);
281
+
282
+ strong {
283
+ font-weight: 700;
284
+ color: #b45309;
285
+ font-size: 16px;
286
+ }
287
+ `;
288
+
289
+ const PublicationsList = styled.div`
290
+ display: grid;
291
+ gap: 20px;
292
+ `;
293
+
294
+ const PublicationCard = styled.div`
295
+ padding: 20px;
296
+ background: #faf5ff;
297
+ border-left: 4px solid #a78bfa;
298
+ border-radius: 10px;
299
+
300
+ strong {
301
+ font-weight: 700;
302
+ color: #6b21a8;
303
+ font-size: 16px;
304
+ }
305
+ `;
306
+
307
+ const SimpleList = styled.div`
308
+ display: grid;
309
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
310
+ gap: 16px;
311
+ `;
312
+
313
+ const SimpleCard = styled.div`
314
+ padding: 16px;
315
+ background: #f0fdf4;
316
+ border: 2px solid #86efac;
317
+ border-radius: 12px;
318
+ font-size: 15px;
319
+ line-height: 1.7;
320
+
321
+ strong {
322
+ font-weight: 700;
323
+ color: #166534;
324
+ }
325
+ `;
326
+
327
+ function Resume({ resume }) {
328
+ const {
329
+ basics = {},
330
+ work = [],
331
+ education = [],
332
+ skills = [],
333
+ projects = [],
334
+ volunteer = [],
335
+ awards = [],
336
+ publications = [],
337
+ certificates = [],
338
+ languages = [],
339
+ interests = [],
340
+ references = [],
341
+ } = resume;
342
+
343
+ return (
344
+ <Layout>
345
+ <Header>
346
+ {basics.name && <Name>{basics.name}</Name>}
347
+ {basics.label && <Label>{basics.label}</Label>}
348
+ <ContactWrapper>
349
+ {basics.email && (
350
+ <ContactInfo type="email">{basics.email}</ContactInfo>
351
+ )}
352
+ {basics.phone && (
353
+ <ContactInfo type="phone">{basics.phone}</ContactInfo>
354
+ )}
355
+ {basics.location?.city && basics.location?.region && (
356
+ <ContactInfo type="location">
357
+ {basics.location.city}, {basics.location.region}
358
+ </ContactInfo>
359
+ )}
360
+ {basics.url && (
361
+ <ContactInfo type="url">
362
+ <a href={safeUrl(basics.url)}>{basics.url}</a>
363
+ </ContactInfo>
364
+ )}
365
+ {basics.profiles?.map((profile, index) => (
366
+ <ContactInfo key={index} type="social">
367
+ <a href={safeUrl(profile.url)}>{profile.network}</a>
368
+ </ContactInfo>
369
+ ))}
370
+ </ContactWrapper>
371
+ </Header>
372
+
373
+ {basics.summary && (
374
+ <SummarySection>
375
+ <SummaryText>"{basics.summary}"</SummaryText>
376
+ </SummarySection>
377
+ )}
378
+
379
+ {certificates && certificates.length > 0 && (
380
+ <MainSection>
381
+ <MainSectionTitle>Certifications & Diplomas</MainSectionTitle>
382
+ <CertificationsGrid>
383
+ {certificates.map((cert, index) => (
384
+ <CertCard key={index}>
385
+ <CertName>{cert.name}</CertName>
386
+ {cert.issuer && (
387
+ <CertIssuer>Issued by: {cert.issuer}</CertIssuer>
388
+ )}
389
+ {cert.date && <CertDate>{cert.date}</CertDate>}
390
+ </CertCard>
391
+ ))}
392
+ </CertificationsGrid>
393
+ </MainSection>
394
+ )}
395
+
396
+ {volunteer.length > 0 && (
397
+ <MainSection>
398
+ <MainSectionTitle>Community Involvement</MainSectionTitle>
399
+ {volunteer.map((vol, index) => (
400
+ <VolunteerItem key={index}>
401
+ <WorkHeader>
402
+ <WorkTitle>{vol.position}</WorkTitle>
403
+ <WorkMeta>
404
+ <DateRange startDate={vol.startDate} endDate={vol.endDate} />
405
+ </WorkMeta>
406
+ </WorkHeader>
407
+ <WorkCompany>{vol.organization}</WorkCompany>
408
+ {vol.summary && <WorkDescription>{vol.summary}</WorkDescription>}
409
+ {vol.highlights && vol.highlights.length > 0 && (
410
+ <WorkHighlights>
411
+ {vol.highlights.map((highlight, i) => (
412
+ <li key={i}>{highlight}</li>
413
+ ))}
414
+ </WorkHighlights>
415
+ )}
416
+ </VolunteerItem>
417
+ ))}
418
+ </MainSection>
419
+ )}
420
+
421
+ {work.length > 0 && (
422
+ <MainSection>
423
+ <MainSectionTitle>Professional Experience</MainSectionTitle>
424
+ {work.map((job, index) => (
425
+ <WorkItem key={index}>
426
+ <WorkHeader>
427
+ <WorkTitle>{job.position || job.name}</WorkTitle>
428
+ <WorkMeta>
429
+ <DateRange startDate={job.startDate} endDate={job.endDate} />
430
+ </WorkMeta>
431
+ </WorkHeader>
432
+ {job.name && <WorkCompany>{job.name}</WorkCompany>}
433
+ {job.summary && <WorkDescription>{job.summary}</WorkDescription>}
434
+ {job.highlights && job.highlights.length > 0 && (
435
+ <WorkHighlights>
436
+ {job.highlights.map((highlight, i) => (
437
+ <li key={i}>{highlight}</li>
438
+ ))}
439
+ </WorkHighlights>
440
+ )}
441
+ </WorkItem>
442
+ ))}
443
+ </MainSection>
444
+ )}
445
+
446
+ {education.length > 0 && (
447
+ <MainSection>
448
+ <MainSectionTitle>Education</MainSectionTitle>
449
+ {education.map((edu, index) => (
450
+ <EducationItem key={index}>
451
+ <WorkHeader>
452
+ <WorkTitle>{edu.institution}</WorkTitle>
453
+ <WorkMeta>
454
+ <DateRange startDate={edu.startDate} endDate={edu.endDate} />
455
+ </WorkMeta>
456
+ </WorkHeader>
457
+ {edu.studyType && edu.area && (
458
+ <WorkCompany>
459
+ {edu.studyType} in {edu.area}
460
+ </WorkCompany>
461
+ )}
462
+ {edu.score && <WorkDescription>GPA: {edu.score}</WorkDescription>}
463
+ </EducationItem>
464
+ ))}
465
+ </MainSection>
466
+ )}
467
+
468
+ {skills.length > 0 && (
469
+ <MainSection>
470
+ <MainSectionTitle>Skills</MainSectionTitle>
471
+ <SkillsGrid>
472
+ {skills.map((skill, index) => (
473
+ <SkillCard key={index}>
474
+ <SkillName>{skill.name}</SkillName>
475
+ {skill.keywords && skill.keywords.length > 0 && (
476
+ <SkillKeywords>
477
+ {skill.keywords.map((keyword, i) => (
478
+ <SkillBadge key={i}>{keyword}</SkillBadge>
479
+ ))}
480
+ </SkillKeywords>
481
+ )}
482
+ </SkillCard>
483
+ ))}
484
+ </SkillsGrid>
485
+ </MainSection>
486
+ )}
487
+
488
+ {projects.length > 0 && (
489
+ <MainSection>
490
+ <MainSectionTitle>Projects</MainSectionTitle>
491
+ {projects.map((project, index) => (
492
+ <WorkItem key={index}>
493
+ <WorkHeader>
494
+ <WorkTitle>
495
+ {project.url ? (
496
+ <Link href={safeUrl(project.url)}>{project.name}</Link>
497
+ ) : (
498
+ project.name
499
+ )}
500
+ </WorkTitle>
501
+ {(project.startDate || project.endDate) && (
502
+ <WorkMeta>
503
+ <DateRange
504
+ startDate={project.startDate}
505
+ endDate={project.endDate}
506
+ />
507
+ </WorkMeta>
508
+ )}
509
+ </WorkHeader>
510
+ {project.description && (
511
+ <WorkDescription>{project.description}</WorkDescription>
512
+ )}
513
+ {project.highlights && project.highlights.length > 0 && (
514
+ <WorkHighlights>
515
+ {project.highlights.map((highlight, i) => (
516
+ <li key={i}>{highlight}</li>
517
+ ))}
518
+ </WorkHighlights>
519
+ )}
520
+ </WorkItem>
521
+ ))}
522
+ </MainSection>
523
+ )}
524
+
525
+ {awards.length > 0 && (
526
+ <MainSection>
527
+ <MainSectionTitle>Awards & Recognition</MainSectionTitle>
528
+ <AwardsList>
529
+ {awards.map((award, index) => (
530
+ <AwardCard key={index}>
531
+ <strong>{award.title}</strong>
532
+ {award.awarder && (
533
+ <div style={{ marginTop: '8px', color: '#92400e' }}>
534
+ — {award.awarder}
535
+ </div>
536
+ )}
537
+ {award.date && (
538
+ <div
539
+ style={{
540
+ fontSize: '13px',
541
+ color: '#78716c',
542
+ marginTop: '6px',
543
+ fontStyle: 'italic',
544
+ }}
545
+ >
546
+ {award.date}
547
+ </div>
548
+ )}
549
+ </AwardCard>
550
+ ))}
551
+ </AwardsList>
552
+ </MainSection>
553
+ )}
554
+
555
+ {publications.length > 0 && (
556
+ <MainSection>
557
+ <MainSectionTitle>Publications</MainSectionTitle>
558
+ <PublicationsList>
559
+ {publications.map((pub, index) => (
560
+ <PublicationCard key={index}>
561
+ <strong>{pub.name}</strong>
562
+ {pub.publisher && (
563
+ <div style={{ marginTop: '8px', color: '#5b21b6' }}>
564
+ — {pub.publisher}
565
+ </div>
566
+ )}
567
+ {pub.releaseDate && (
568
+ <div
569
+ style={{
570
+ fontSize: '13px',
571
+ color: '#6b7280',
572
+ marginTop: '6px',
573
+ fontStyle: 'italic',
574
+ }}
575
+ >
576
+ {pub.releaseDate}
577
+ </div>
578
+ )}
579
+ </PublicationCard>
580
+ ))}
581
+ </PublicationsList>
582
+ </MainSection>
583
+ )}
584
+
585
+ {languages.length > 0 && (
586
+ <MainSection>
587
+ <MainSectionTitle>Languages</MainSectionTitle>
588
+ <SimpleList>
589
+ {languages.map((lang, index) => (
590
+ <SimpleCard key={index}>
591
+ <strong>{lang.language}</strong>
592
+ {lang.fluency && ` — ${lang.fluency}`}
593
+ </SimpleCard>
594
+ ))}
595
+ </SimpleList>
596
+ </MainSection>
597
+ )}
598
+
599
+ {interests.length > 0 && (
600
+ <MainSection>
601
+ <MainSectionTitle>Interests</MainSectionTitle>
602
+ <SimpleList>
603
+ {interests.map((interest, index) => (
604
+ <SimpleCard key={index}>{interest.name}</SimpleCard>
605
+ ))}
606
+ </SimpleList>
607
+ </MainSection>
608
+ )}
609
+
610
+ {references.length > 0 && (
611
+ <MainSection>
612
+ <MainSectionTitle>References</MainSectionTitle>
613
+ {references.map((ref, index) => (
614
+ <WorkItem key={index}>
615
+ <WorkTitle>{ref.name}</WorkTitle>
616
+ {ref.reference && (
617
+ <WorkDescription>{ref.reference}</WorkDescription>
618
+ )}
619
+ </WorkItem>
620
+ ))}
621
+ </MainSection>
622
+ )}
623
+ </Layout>
624
+ );
625
+ }
626
+
627
+ export default Resume;