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