jsonresume-theme-operations-precision 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/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # Operations Precision - JSON Resume Theme
2
+
3
+ A clean, efficient JSON Resume theme designed for operations professionals, systems thinkers, and process-oriented individuals.
4
+
5
+ ## Design Philosophy
6
+
7
+ **Vibe:** Efficient, dependable, systems-oriented
8
+ **Layout:** Single column with clean structure and efficient use of space
9
+ **Typography:** Sans-serif (Inter), compact but readable (15px body text)
10
+ **Color:** Teal accent (#0f766e) representing operational efficiency
11
+
12
+ ## Key Features
13
+
14
+ - **Compact Design**: Efficient spacing optimized for readability without wasted space
15
+ - **Tabular Skills Layout**: Skills displayed in a clean grid format with clear labels
16
+ - **Process-Focused**: Emphasizes systematic approach and measurable improvements
17
+ - **Professional Typography**: Upper-case section titles with consistent letter spacing
18
+ - **Numeric Precision**: Tabular numbers for dates ensure perfect alignment
19
+ - **Print-Optimized**: Clean structure that prints beautifully
20
+
21
+ ## Perfect For
22
+
23
+ - Operations Managers
24
+ - Logistics Professionals
25
+ - Process Engineers
26
+ - Systems Analysts
27
+ - Project Managers
28
+ - Quality Assurance Specialists
29
+
30
+ ## Supported Sections
31
+
32
+ All 12 standard JSON Resume sections are fully supported:
33
+
34
+ - Basics (name, contact, summary)
35
+ - Work Experience
36
+ - Skills & Tools (tabular layout)
37
+ - Education
38
+ - Projects
39
+ - Volunteer
40
+ - Awards
41
+ - Publications
42
+ - Languages (two-column grid)
43
+ - Interests (two-column grid)
44
+ - References
45
+
46
+ ## Design Details
47
+
48
+ ### Color Palette
49
+
50
+ - **Primary Accent**: `#0f766e` (teal)
51
+ - **Headings**: `#111827` (near-black)
52
+ - **Body Text**: `#4b5563` (dark gray)
53
+ - **Metadata**: `#6b7280` (medium gray)
54
+ - **Background**: White
55
+
56
+ ### Typography
57
+
58
+ - **Font Family**: Inter (with system font fallbacks)
59
+ - **Body Size**: 15px (10.5pt equivalent)
60
+ - **Line Height**: 1.5-1.6 (compact but readable)
61
+ - **Section Titles**: Uppercase with increased letter spacing
62
+ - **Emphasis**: Tabular numbers for date alignment
63
+
64
+ ### Layout Features
65
+
66
+ - **Max Width**: 850px for optimal readability
67
+ - **Skills Table**: 140px label column + flexible content
68
+ - **Two-Column Grids**: Used for languages and interests
69
+ - **Page Break Control**: Sections avoid breaking across pages
70
+ - **Responsive**: Adapts gracefully to mobile screens
71
+
72
+ ## License
73
+
74
+ MIT
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "jsonresume-theme-operations-precision",
3
+ "version": "0.1.0",
4
+ "description": "Operations professionals - show your systematic approach, process improvements",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "jsonresume",
10
+ "theme",
11
+ "operations",
12
+ "precision",
13
+ "efficiency",
14
+ "systems"
15
+ ],
16
+ "peerDependencies": {
17
+ "react": "^18.0.0 || ^19.0.0",
18
+ "react-dom": "^18.0.0 || ^19.0.0"
19
+ },
20
+ "dependencies": {
21
+ "styled-components": "^6.1.19",
22
+ "@resume/core": "0.1.0"
23
+ }
24
+ }
package/src/Resume.jsx ADDED
@@ -0,0 +1,478 @@
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
+ max-width: 850px;
7
+ margin: 0 auto;
8
+ padding: 40px 35px;
9
+ background: white;
10
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',
11
+ sans-serif;
12
+ color: #1f2937;
13
+ font-size: 15px;
14
+ line-height: 1.5;
15
+
16
+ @media print {
17
+ padding: 30px;
18
+ }
19
+ `;
20
+
21
+ const Header = styled.header`
22
+ margin-bottom: 35px;
23
+ padding-bottom: 20px;
24
+ border-bottom: 2px solid #0f766e;
25
+ `;
26
+
27
+ const Name = styled.h1`
28
+ font-size: 32px;
29
+ font-weight: 700;
30
+ color: #0f766e;
31
+ margin: 0 0 6px 0;
32
+ letter-spacing: -0.3px;
33
+ `;
34
+
35
+ const Label = styled.div`
36
+ font-size: 15px;
37
+ color: #4b5563;
38
+ margin-bottom: 16px;
39
+ font-weight: 500;
40
+ text-transform: uppercase;
41
+ letter-spacing: 0.5px;
42
+ `;
43
+
44
+ const StyledContactInfo = styled(ContactInfo)`
45
+ font-size: 14px;
46
+ display: flex;
47
+ flex-wrap: wrap;
48
+ gap: 12px;
49
+
50
+ a {
51
+ font-size: 14px;
52
+ color: #0f766e;
53
+ text-decoration: none;
54
+
55
+ &:hover {
56
+ text-decoration: underline;
57
+ }
58
+ }
59
+ `;
60
+
61
+ const Summary = styled.p`
62
+ font-size: 15px;
63
+ line-height: 1.6;
64
+ color: #374151;
65
+ margin: 16px 0 0 0;
66
+ text-align: justify;
67
+ `;
68
+
69
+ const StyledSectionTitle = styled(SectionTitle)`
70
+ font-size: 16px;
71
+ font-weight: 700;
72
+ color: #0f766e;
73
+ margin: 28px 0 16px 0;
74
+ padding-bottom: 6px;
75
+ border-bottom: 2px solid #0f766e;
76
+ text-transform: uppercase;
77
+ letter-spacing: 0.8px;
78
+ `;
79
+
80
+ const WorkItem = styled.div`
81
+ margin-bottom: 24px;
82
+ page-break-inside: avoid;
83
+
84
+ &:last-child {
85
+ margin-bottom: 0;
86
+ }
87
+ `;
88
+
89
+ const WorkHeader = styled.div`
90
+ display: grid;
91
+ grid-template-columns: 1fr auto;
92
+ gap: 12px;
93
+ margin-bottom: 6px;
94
+ align-items: baseline;
95
+
96
+ @media (max-width: 640px) {
97
+ grid-template-columns: 1fr;
98
+ gap: 4px;
99
+ }
100
+ `;
101
+
102
+ const Position = styled.h3`
103
+ font-size: 16px;
104
+ font-weight: 600;
105
+ color: #111827;
106
+ margin: 0;
107
+ `;
108
+
109
+ const Company = styled.div`
110
+ font-size: 14px;
111
+ color: #0f766e;
112
+ font-weight: 500;
113
+ margin-top: 2px;
114
+ `;
115
+
116
+ const DateText = styled.div`
117
+ font-size: 13px;
118
+ color: #6b7280;
119
+ font-weight: 500;
120
+ white-space: nowrap;
121
+ font-variant-numeric: tabular-nums;
122
+ `;
123
+
124
+ const WorkSummary = styled.p`
125
+ margin: 8px 0;
126
+ color: #4b5563;
127
+ line-height: 1.6;
128
+ font-size: 14px;
129
+ `;
130
+
131
+ const Highlights = styled.ul`
132
+ margin: 8px 0 0 0;
133
+ padding-left: 18px;
134
+ list-style-type: square;
135
+
136
+ li {
137
+ margin: 6px 0;
138
+ color: #4b5563;
139
+ line-height: 1.6;
140
+ font-size: 14px;
141
+ padding-left: 4px;
142
+ }
143
+ `;
144
+
145
+ const EducationItem = styled.div`
146
+ margin-bottom: 20px;
147
+ page-break-inside: avoid;
148
+
149
+ &:last-child {
150
+ margin-bottom: 0;
151
+ }
152
+ `;
153
+
154
+ const Institution = styled.h3`
155
+ font-size: 15px;
156
+ font-weight: 600;
157
+ color: #111827;
158
+ margin: 0 0 4px 0;
159
+ `;
160
+
161
+ const Degree = styled.div`
162
+ font-size: 14px;
163
+ color: #4b5563;
164
+ margin-bottom: 3px;
165
+ `;
166
+
167
+ const EducationDate = styled.div`
168
+ font-size: 13px;
169
+ color: #6b7280;
170
+ font-variant-numeric: tabular-nums;
171
+ `;
172
+
173
+ const SkillsTable = styled.div`
174
+ display: grid;
175
+ grid-template-columns: 140px 1fr;
176
+ gap: 12px 20px;
177
+ border: 1px solid #e5e7eb;
178
+ padding: 16px;
179
+ background: #f9fafb;
180
+
181
+ @media (max-width: 640px) {
182
+ grid-template-columns: 1fr;
183
+ gap: 12px;
184
+ }
185
+ `;
186
+
187
+ const SkillName = styled.div`
188
+ font-size: 14px;
189
+ font-weight: 600;
190
+ color: #0f766e;
191
+ text-transform: uppercase;
192
+ letter-spacing: 0.5px;
193
+ align-self: start;
194
+ padding-top: 2px;
195
+ `;
196
+
197
+ const SkillTags = styled.div`
198
+ font-size: 14px;
199
+ color: #4b5563;
200
+ line-height: 1.5;
201
+ `;
202
+
203
+ const ProjectItem = styled.div`
204
+ margin-bottom: 20px;
205
+ page-break-inside: avoid;
206
+
207
+ &:last-child {
208
+ margin-bottom: 0;
209
+ }
210
+ `;
211
+
212
+ const ProjectHeader = styled.div`
213
+ display: flex;
214
+ justify-content: space-between;
215
+ align-items: baseline;
216
+ gap: 12px;
217
+ margin-bottom: 6px;
218
+
219
+ @media (max-width: 640px) {
220
+ flex-direction: column;
221
+ gap: 4px;
222
+ }
223
+ `;
224
+
225
+ const ProjectName = styled.h3`
226
+ font-size: 15px;
227
+ font-weight: 600;
228
+ color: #111827;
229
+ margin: 0;
230
+ `;
231
+
232
+ const TwoColumnGrid = styled.div`
233
+ display: grid;
234
+ grid-template-columns: repeat(2, 1fr);
235
+ gap: 12px 20px;
236
+
237
+ @media (max-width: 640px) {
238
+ grid-template-columns: 1fr;
239
+ }
240
+ `;
241
+
242
+ const CompactItem = styled.div`
243
+ font-size: 14px;
244
+ line-height: 1.5;
245
+ color: #4b5563;
246
+ `;
247
+
248
+ const CompactTitle = styled.span`
249
+ font-weight: 600;
250
+ color: #111827;
251
+ `;
252
+
253
+ function Resume({ resume }) {
254
+ const {
255
+ basics = {},
256
+ work = [],
257
+ education = [],
258
+ skills = [],
259
+ projects = [],
260
+ volunteer = [],
261
+ awards = [],
262
+ publications = [],
263
+ languages = [],
264
+ interests = [],
265
+ references = [],
266
+ } = resume;
267
+
268
+ return (
269
+ <Layout>
270
+ <Header>
271
+ <Name>{basics.name}</Name>
272
+ {basics.label && <Label>{basics.label}</Label>}
273
+ <StyledContactInfo basics={basics} />
274
+ {basics.summary && <Summary>{basics.summary}</Summary>}
275
+ </Header>
276
+
277
+ {work?.length > 0 && (
278
+ <Section>
279
+ <StyledSectionTitle>Experience</StyledSectionTitle>
280
+ {work.map((job, index) => (
281
+ <WorkItem key={index}>
282
+ <WorkHeader>
283
+ <div>
284
+ <Position>{job.position}</Position>
285
+ {job.name && <Company>{job.name}</Company>}
286
+ </div>
287
+ <DateText>
288
+ <DateRange startDate={job.startDate} endDate={job.endDate} />
289
+ </DateText>
290
+ </WorkHeader>
291
+ {job.summary && <WorkSummary>{job.summary}</WorkSummary>}
292
+ {job.highlights?.length > 0 && (
293
+ <Highlights>
294
+ {job.highlights.map((highlight, i) => (
295
+ <li key={i}>{highlight}</li>
296
+ ))}
297
+ </Highlights>
298
+ )}
299
+ </WorkItem>
300
+ ))}
301
+ </Section>
302
+ )}
303
+
304
+ {skills?.length > 0 && (
305
+ <Section>
306
+ <StyledSectionTitle>Skills & Tools</StyledSectionTitle>
307
+ <SkillsTable>
308
+ {skills.map((skill, index) => (
309
+ <React.Fragment key={index}>
310
+ <SkillName>{skill.name}</SkillName>
311
+ <SkillTags>
312
+ {skill.keywords?.length > 0
313
+ ? skill.keywords.join(' • ')
314
+ : '—'}
315
+ </SkillTags>
316
+ </React.Fragment>
317
+ ))}
318
+ </SkillsTable>
319
+ </Section>
320
+ )}
321
+
322
+ {education?.length > 0 && (
323
+ <Section>
324
+ <StyledSectionTitle>Education</StyledSectionTitle>
325
+ {education.map((edu, index) => (
326
+ <EducationItem key={index}>
327
+ <Institution>{edu.institution}</Institution>
328
+ <Degree>
329
+ {edu.studyType} in {edu.area}
330
+ {edu.score && ` • ${edu.score}`}
331
+ </Degree>
332
+ <EducationDate>
333
+ <DateRange startDate={edu.startDate} endDate={edu.endDate} />
334
+ </EducationDate>
335
+ </EducationItem>
336
+ ))}
337
+ </Section>
338
+ )}
339
+
340
+ {projects?.length > 0 && (
341
+ <Section>
342
+ <StyledSectionTitle>Projects</StyledSectionTitle>
343
+ {projects.map((project, index) => (
344
+ <ProjectItem key={index}>
345
+ <ProjectHeader>
346
+ <ProjectName>{project.name}</ProjectName>
347
+ {(project.startDate || project.endDate) && (
348
+ <DateText>
349
+ <DateRange
350
+ startDate={project.startDate}
351
+ endDate={project.endDate}
352
+ />
353
+ </DateText>
354
+ )}
355
+ </ProjectHeader>
356
+ {project.description && (
357
+ <WorkSummary>{project.description}</WorkSummary>
358
+ )}
359
+ {project.highlights?.length > 0 && (
360
+ <Highlights>
361
+ {project.highlights.map((highlight, i) => (
362
+ <li key={i}>{highlight}</li>
363
+ ))}
364
+ </Highlights>
365
+ )}
366
+ </ProjectItem>
367
+ ))}
368
+ </Section>
369
+ )}
370
+
371
+ {volunteer?.length > 0 && (
372
+ <Section>
373
+ <StyledSectionTitle>Volunteer</StyledSectionTitle>
374
+ {volunteer.map((vol, index) => (
375
+ <WorkItem key={index}>
376
+ <WorkHeader>
377
+ <div>
378
+ <Position>{vol.position}</Position>
379
+ {vol.organization && <Company>{vol.organization}</Company>}
380
+ </div>
381
+ {(vol.startDate || vol.endDate) && (
382
+ <DateText>
383
+ <DateRange
384
+ startDate={vol.startDate}
385
+ endDate={vol.endDate}
386
+ />
387
+ </DateText>
388
+ )}
389
+ </WorkHeader>
390
+ {vol.summary && <WorkSummary>{vol.summary}</WorkSummary>}
391
+ {vol.highlights?.length > 0 && (
392
+ <Highlights>
393
+ {vol.highlights.map((highlight, i) => (
394
+ <li key={i}>{highlight}</li>
395
+ ))}
396
+ </Highlights>
397
+ )}
398
+ </WorkItem>
399
+ ))}
400
+ </Section>
401
+ )}
402
+
403
+ {awards?.length > 0 && (
404
+ <Section>
405
+ <StyledSectionTitle>Awards</StyledSectionTitle>
406
+ {awards.map((award, index) => (
407
+ <EducationItem key={index}>
408
+ <Institution>{award.title}</Institution>
409
+ {award.awarder && <Degree>Awarded by {award.awarder}</Degree>}
410
+ {award.date && <EducationDate>{award.date}</EducationDate>}
411
+ {award.summary && <WorkSummary>{award.summary}</WorkSummary>}
412
+ </EducationItem>
413
+ ))}
414
+ </Section>
415
+ )}
416
+
417
+ {publications?.length > 0 && (
418
+ <Section>
419
+ <StyledSectionTitle>Publications</StyledSectionTitle>
420
+ {publications.map((pub, index) => (
421
+ <EducationItem key={index}>
422
+ <Institution>{pub.name}</Institution>
423
+ {pub.publisher && <Degree>Published by {pub.publisher}</Degree>}
424
+ {pub.releaseDate && (
425
+ <EducationDate>{pub.releaseDate}</EducationDate>
426
+ )}
427
+ {pub.summary && <WorkSummary>{pub.summary}</WorkSummary>}
428
+ </EducationItem>
429
+ ))}
430
+ </Section>
431
+ )}
432
+
433
+ {languages?.length > 0 && (
434
+ <Section>
435
+ <StyledSectionTitle>Languages</StyledSectionTitle>
436
+ <TwoColumnGrid>
437
+ {languages.map((lang, index) => (
438
+ <CompactItem key={index}>
439
+ <CompactTitle>{lang.language}:</CompactTitle>{' '}
440
+ {lang.fluency || '—'}
441
+ </CompactItem>
442
+ ))}
443
+ </TwoColumnGrid>
444
+ </Section>
445
+ )}
446
+
447
+ {interests?.length > 0 && (
448
+ <Section>
449
+ <StyledSectionTitle>Interests</StyledSectionTitle>
450
+ <TwoColumnGrid>
451
+ {interests.map((interest, index) => (
452
+ <CompactItem key={index}>
453
+ <CompactTitle>{interest.name}</CompactTitle>
454
+ {interest.keywords?.length > 0 && (
455
+ <>: {interest.keywords.join(', ')}</>
456
+ )}
457
+ </CompactItem>
458
+ ))}
459
+ </TwoColumnGrid>
460
+ </Section>
461
+ )}
462
+
463
+ {references?.length > 0 && (
464
+ <Section>
465
+ <StyledSectionTitle>References</StyledSectionTitle>
466
+ {references.map((ref, index) => (
467
+ <EducationItem key={index}>
468
+ <Institution>{ref.name}</Institution>
469
+ {ref.reference && <WorkSummary>{ref.reference}</WorkSummary>}
470
+ </EducationItem>
471
+ ))}
472
+ </Section>
473
+ )}
474
+ </Layout>
475
+ );
476
+ }
477
+
478
+ 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;700&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: #f5f5f5;
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
+ }