jsonresume-theme-coastal-creative 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 +481 -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=Inter:wght@400;500;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-coastal-creative",
3
+ "version": "0.1.0",
4
+ "description": "Calm, spacious theme with ocean blue accents and generous line heights. Design-forward yet approachable.",
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,481 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import {
4
+ Section,
5
+ SectionTitle,
6
+ DateRange,
7
+ Badge,
8
+ BadgeList,
9
+ ContactInfo,
10
+ Link,
11
+ safeUrl,
12
+ } from '@resume/core';
13
+
14
+ const Layout = styled.div`
15
+ max-width: 850px;
16
+ margin: 0 auto;
17
+ padding: 56px 40px;
18
+ background: #fdfdf9;
19
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
20
+ color: #334155;
21
+ line-height: 1.9;
22
+ font-size: 16px;
23
+
24
+ @media print {
25
+ padding: 32px;
26
+ background: white;
27
+ }
28
+ `;
29
+
30
+ const Header = styled.header`
31
+ margin-bottom: 56px;
32
+ text-align: center;
33
+ `;
34
+
35
+ const Name = styled.h1`
36
+ font-size: 42px;
37
+ font-weight: 600;
38
+ margin: 0 0 12px 0;
39
+ color: #0f172a;
40
+ letter-spacing: -0.5px;
41
+ `;
42
+
43
+ const Label = styled.div`
44
+ font-size: 18px;
45
+ color: #0284c7;
46
+ font-weight: 500;
47
+ margin-bottom: 20px;
48
+ `;
49
+
50
+ const ContactWrapper = styled.div`
51
+ display: flex;
52
+ justify-content: center;
53
+ flex-wrap: wrap;
54
+ gap: 20px;
55
+ font-size: 15px;
56
+ color: #64748b;
57
+ `;
58
+
59
+ const SummarySection = styled(Section)`
60
+ margin-bottom: 48px;
61
+ padding: 32px;
62
+ background: white;
63
+ border-radius: 12px;
64
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
65
+ border-left: 4px solid #0284c7;
66
+ `;
67
+
68
+ const SummaryText = styled.p`
69
+ margin: 0;
70
+ font-size: 17px;
71
+ line-height: 2;
72
+ color: #475569;
73
+ `;
74
+
75
+ const MainSection = styled(Section)`
76
+ margin-bottom: 48px;
77
+ `;
78
+
79
+ const MainSectionTitle = styled(SectionTitle)`
80
+ font-size: 24px;
81
+ font-weight: 600;
82
+ color: #0284c7;
83
+ margin: 0 0 32px 0;
84
+ text-transform: uppercase;
85
+ letter-spacing: 1px;
86
+ `;
87
+
88
+ const WorkItem = styled.div`
89
+ margin-bottom: 40px;
90
+ padding: 28px;
91
+ background: white;
92
+ border-radius: 12px;
93
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
94
+
95
+ &:last-child {
96
+ margin-bottom: 0;
97
+ }
98
+ `;
99
+
100
+ const WorkHeader = styled.div`
101
+ display: flex;
102
+ justify-content: space-between;
103
+ align-items: baseline;
104
+ margin-bottom: 12px;
105
+ flex-wrap: wrap;
106
+ gap: 12px;
107
+ `;
108
+
109
+ const WorkTitle = styled.h3`
110
+ font-size: 20px;
111
+ font-weight: 600;
112
+ margin: 0;
113
+ color: #0f172a;
114
+ `;
115
+
116
+ const WorkMeta = styled.div`
117
+ font-size: 14px;
118
+ color: #94a3b8;
119
+ font-weight: 500;
120
+ `;
121
+
122
+ const WorkCompany = styled.div`
123
+ font-size: 16px;
124
+ color: #0284c7;
125
+ font-weight: 500;
126
+ margin-bottom: 12px;
127
+ `;
128
+
129
+ const WorkDescription = styled.p`
130
+ font-size: 16px;
131
+ line-height: 1.9;
132
+ margin: 16px 0;
133
+ color: #475569;
134
+ `;
135
+
136
+ const WorkHighlights = styled.ul`
137
+ margin: 16px 0 0 0;
138
+ padding-left: 24px;
139
+ list-style: disc;
140
+
141
+ li {
142
+ margin-bottom: 10px;
143
+ font-size: 16px;
144
+ line-height: 1.9;
145
+ color: #475569;
146
+
147
+ &::marker {
148
+ color: #0284c7;
149
+ }
150
+ }
151
+ `;
152
+
153
+ const SkillsGrid = styled.div`
154
+ display: grid;
155
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
156
+ gap: 24px;
157
+ `;
158
+
159
+ const SkillCard = styled.div`
160
+ padding: 24px;
161
+ background: white;
162
+ border-radius: 12px;
163
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
164
+ `;
165
+
166
+ const SkillName = styled.h4`
167
+ font-size: 15px;
168
+ font-weight: 600;
169
+ margin: 0 0 16px 0;
170
+ color: #0284c7;
171
+ text-transform: uppercase;
172
+ letter-spacing: 0.5px;
173
+ `;
174
+
175
+ const StyledBadgeList = styled(BadgeList)`
176
+ display: flex;
177
+ flex-wrap: wrap;
178
+ gap: 8px;
179
+ `;
180
+
181
+ const StyledBadge = styled(Badge)`
182
+ font-size: 13px;
183
+ padding: 8px 14px;
184
+ background: #f0f9ff;
185
+ border: none;
186
+ color: #0369a1;
187
+ font-weight: 500;
188
+ border-radius: 20px;
189
+ `;
190
+
191
+ const SimpleList = styled.div`
192
+ display: grid;
193
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
194
+ gap: 24px;
195
+ `;
196
+
197
+ const SimpleCard = styled.div`
198
+ padding: 20px;
199
+ background: white;
200
+ border-radius: 12px;
201
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
202
+ font-size: 16px;
203
+ line-height: 1.8;
204
+
205
+ strong {
206
+ font-weight: 600;
207
+ color: #0f172a;
208
+ }
209
+ `;
210
+
211
+ function Resume({ resume }) {
212
+ const {
213
+ basics = {},
214
+ work = [],
215
+ education = [],
216
+ skills = [],
217
+ projects = [],
218
+ volunteer = [],
219
+ awards = [],
220
+ publications = [],
221
+ languages = [],
222
+ interests = [],
223
+ references = [],
224
+ } = resume;
225
+
226
+ return (
227
+ <Layout>
228
+ <Header>
229
+ {basics.name && <Name>{basics.name}</Name>}
230
+ {basics.label && <Label>{basics.label}</Label>}
231
+ <ContactWrapper>
232
+ {basics.email && (
233
+ <ContactInfo type="email">{basics.email}</ContactInfo>
234
+ )}
235
+ {basics.phone && (
236
+ <ContactInfo type="phone">{basics.phone}</ContactInfo>
237
+ )}
238
+ {basics.location?.city && basics.location?.region && (
239
+ <ContactInfo type="location">
240
+ {basics.location.city}, {basics.location.region}
241
+ </ContactInfo>
242
+ )}
243
+ {basics.url && (
244
+ <ContactInfo type="url">
245
+ <a href={safeUrl(basics.url)}>{basics.url}</a>
246
+ </ContactInfo>
247
+ )}
248
+ {basics.profiles?.map((profile, index) => (
249
+ <ContactInfo key={index} type="social">
250
+ <a href={safeUrl(profile.url)}>{profile.network}</a>
251
+ </ContactInfo>
252
+ ))}
253
+ </ContactWrapper>
254
+ </Header>
255
+
256
+ {basics.summary && (
257
+ <SummarySection>
258
+ <SummaryText>{basics.summary}</SummaryText>
259
+ </SummarySection>
260
+ )}
261
+
262
+ {work.length > 0 && (
263
+ <MainSection>
264
+ <MainSectionTitle>Experience</MainSectionTitle>
265
+ {work.map((job, index) => (
266
+ <WorkItem key={index}>
267
+ <WorkHeader>
268
+ <WorkTitle>{job.position || job.name}</WorkTitle>
269
+ <WorkMeta>
270
+ <DateRange startDate={job.startDate} endDate={job.endDate} />
271
+ </WorkMeta>
272
+ </WorkHeader>
273
+ {job.name && <WorkCompany>{job.name}</WorkCompany>}
274
+ {job.summary && <WorkDescription>{job.summary}</WorkDescription>}
275
+ {job.highlights && job.highlights.length > 0 && (
276
+ <WorkHighlights>
277
+ {job.highlights.map((highlight, i) => (
278
+ <li key={i}>{highlight}</li>
279
+ ))}
280
+ </WorkHighlights>
281
+ )}
282
+ </WorkItem>
283
+ ))}
284
+ </MainSection>
285
+ )}
286
+
287
+ {skills.length > 0 && (
288
+ <MainSection>
289
+ <MainSectionTitle>Skills</MainSectionTitle>
290
+ <SkillsGrid>
291
+ {skills.map((skill, index) => (
292
+ <SkillCard key={index}>
293
+ <SkillName>{skill.name}</SkillName>
294
+ <StyledBadgeList>
295
+ {skill.keywords?.map((keyword, i) => (
296
+ <StyledBadge key={i}>{keyword}</StyledBadge>
297
+ ))}
298
+ </StyledBadgeList>
299
+ </SkillCard>
300
+ ))}
301
+ </SkillsGrid>
302
+ </MainSection>
303
+ )}
304
+
305
+ {projects.length > 0 && (
306
+ <MainSection>
307
+ <MainSectionTitle>Projects</MainSectionTitle>
308
+ {projects.map((project, index) => (
309
+ <WorkItem key={index}>
310
+ <WorkHeader>
311
+ <WorkTitle>
312
+ {project.url ? (
313
+ <Link href={safeUrl(project.url)}>{project.name}</Link>
314
+ ) : (
315
+ project.name
316
+ )}
317
+ </WorkTitle>
318
+ {(project.startDate || project.endDate) && (
319
+ <WorkMeta>
320
+ <DateRange
321
+ startDate={project.startDate}
322
+ endDate={project.endDate}
323
+ />
324
+ </WorkMeta>
325
+ )}
326
+ </WorkHeader>
327
+ {project.description && (
328
+ <WorkDescription>{project.description}</WorkDescription>
329
+ )}
330
+ {project.highlights && project.highlights.length > 0 && (
331
+ <WorkHighlights>
332
+ {project.highlights.map((highlight, i) => (
333
+ <li key={i}>{highlight}</li>
334
+ ))}
335
+ </WorkHighlights>
336
+ )}
337
+ </WorkItem>
338
+ ))}
339
+ </MainSection>
340
+ )}
341
+
342
+ {education.length > 0 && (
343
+ <MainSection>
344
+ <MainSectionTitle>Education</MainSectionTitle>
345
+ {education.map((edu, index) => (
346
+ <WorkItem key={index}>
347
+ <WorkHeader>
348
+ <WorkTitle>{edu.institution}</WorkTitle>
349
+ <WorkMeta>
350
+ <DateRange startDate={edu.startDate} endDate={edu.endDate} />
351
+ </WorkMeta>
352
+ </WorkHeader>
353
+ {edu.studyType && edu.area && (
354
+ <WorkCompany>
355
+ {edu.studyType} in {edu.area}
356
+ </WorkCompany>
357
+ )}
358
+ {edu.score && <WorkDescription>GPA: {edu.score}</WorkDescription>}
359
+ </WorkItem>
360
+ ))}
361
+ </MainSection>
362
+ )}
363
+
364
+ {volunteer.length > 0 && (
365
+ <MainSection>
366
+ <MainSectionTitle>Volunteer</MainSectionTitle>
367
+ {volunteer.map((vol, index) => (
368
+ <WorkItem key={index}>
369
+ <WorkHeader>
370
+ <WorkTitle>{vol.position}</WorkTitle>
371
+ <WorkMeta>
372
+ <DateRange startDate={vol.startDate} endDate={vol.endDate} />
373
+ </WorkMeta>
374
+ </WorkHeader>
375
+ <WorkCompany>{vol.organization}</WorkCompany>
376
+ {vol.summary && <WorkDescription>{vol.summary}</WorkDescription>}
377
+ {vol.highlights && vol.highlights.length > 0 && (
378
+ <WorkHighlights>
379
+ {vol.highlights.map((highlight, i) => (
380
+ <li key={i}>{highlight}</li>
381
+ ))}
382
+ </WorkHighlights>
383
+ )}
384
+ </WorkItem>
385
+ ))}
386
+ </MainSection>
387
+ )}
388
+
389
+ {awards.length > 0 && (
390
+ <MainSection>
391
+ <MainSectionTitle>Awards</MainSectionTitle>
392
+ <SimpleList>
393
+ {awards.map((award, index) => (
394
+ <SimpleCard key={index}>
395
+ <strong>{award.title}</strong>
396
+ {award.awarder && ` — ${award.awarder}`}
397
+ {award.date && (
398
+ <div
399
+ style={{
400
+ fontSize: '14px',
401
+ color: '#94a3b8',
402
+ marginTop: '8px',
403
+ }}
404
+ >
405
+ {award.date}
406
+ </div>
407
+ )}
408
+ </SimpleCard>
409
+ ))}
410
+ </SimpleList>
411
+ </MainSection>
412
+ )}
413
+
414
+ {publications.length > 0 && (
415
+ <MainSection>
416
+ <MainSectionTitle>Publications</MainSectionTitle>
417
+ <SimpleList>
418
+ {publications.map((pub, index) => (
419
+ <SimpleCard key={index}>
420
+ <strong>{pub.name}</strong>
421
+ {pub.publisher && ` — ${pub.publisher}`}
422
+ {pub.releaseDate && (
423
+ <div
424
+ style={{
425
+ fontSize: '14px',
426
+ color: '#94a3b8',
427
+ marginTop: '8px',
428
+ }}
429
+ >
430
+ {pub.releaseDate}
431
+ </div>
432
+ )}
433
+ </SimpleCard>
434
+ ))}
435
+ </SimpleList>
436
+ </MainSection>
437
+ )}
438
+
439
+ {languages.length > 0 && (
440
+ <MainSection>
441
+ <MainSectionTitle>Languages</MainSectionTitle>
442
+ <SimpleList>
443
+ {languages.map((lang, index) => (
444
+ <SimpleCard key={index}>
445
+ <strong>{lang.language}</strong>
446
+ {lang.fluency && ` — ${lang.fluency}`}
447
+ </SimpleCard>
448
+ ))}
449
+ </SimpleList>
450
+ </MainSection>
451
+ )}
452
+
453
+ {interests.length > 0 && (
454
+ <MainSection>
455
+ <MainSectionTitle>Interests</MainSectionTitle>
456
+ <SimpleList>
457
+ {interests.map((interest, index) => (
458
+ <SimpleCard key={index}>{interest.name}</SimpleCard>
459
+ ))}
460
+ </SimpleList>
461
+ </MainSection>
462
+ )}
463
+
464
+ {references.length > 0 && (
465
+ <MainSection>
466
+ <MainSectionTitle>References</MainSectionTitle>
467
+ {references.map((ref, index) => (
468
+ <WorkItem key={index}>
469
+ <WorkTitle>{ref.name}</WorkTitle>
470
+ {ref.reference && (
471
+ <WorkDescription>{ref.reference}</WorkDescription>
472
+ )}
473
+ </WorkItem>
474
+ ))}
475
+ </MainSection>
476
+ )}
477
+ </Layout>
478
+ );
479
+ }
480
+
481
+ export default Resume;