jsonresume-theme-architects-portfolio 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,27 @@
1
+ {
2
+ "name": "jsonresume-theme-architects-portfolio",
3
+ "version": "1.0.0",
4
+ "description": "Structural elegance with horizontal keylines and geometric precision like a drafting sheet",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "jsonresume",
10
+ "theme",
11
+ "architect",
12
+ "portfolio",
13
+ "geometric",
14
+ "precision",
15
+ "designer",
16
+ "structural",
17
+ "minimal"
18
+ ],
19
+ "peerDependencies": {
20
+ "react": "^18.0.0 || ^19.0.0",
21
+ "react-dom": "^18.0.0 || ^19.0.0"
22
+ },
23
+ "dependencies": {
24
+ "styled-components": "^6.1.19",
25
+ "@resume/core": "0.1.0"
26
+ }
27
+ }
package/src/Resume.jsx ADDED
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import {
3
+ Layout,
4
+ Header,
5
+ Name,
6
+ Label,
7
+ StyledContactInfo,
8
+ Summary,
9
+ } from './components/layout-styles.jsx';
10
+ import { WorkSection } from './components/WorkSection';
11
+ import { SkillsSection } from './components/SkillsSection';
12
+ import { EducationSection } from './components/EducationSection';
13
+ import {
14
+ ProjectsSection,
15
+ VolunteerSection,
16
+ AwardsSection,
17
+ PublicationsSection,
18
+ LanguagesSection,
19
+ InterestsSection,
20
+ ReferencesSection,
21
+ } from './components/OptionalSections';
22
+
23
+ function Resume({ resume }) {
24
+ const {
25
+ basics = {},
26
+ work = [],
27
+ education = [],
28
+ skills = [],
29
+ projects = [],
30
+ volunteer = [],
31
+ awards = [],
32
+ publications = [],
33
+ languages = [],
34
+ interests = [],
35
+ references = [],
36
+ } = resume;
37
+
38
+ return (
39
+ <Layout>
40
+ <Header>
41
+ <Name>{basics.name}</Name>
42
+ {basics.label && <Label>{basics.label}</Label>}
43
+ <StyledContactInfo basics={basics} />
44
+ {basics.summary && <Summary>{basics.summary}</Summary>}
45
+ </Header>
46
+
47
+ <WorkSection work={work} />
48
+ <SkillsSection skills={skills} />
49
+ <EducationSection education={education} />
50
+ <ProjectsSection projects={projects} />
51
+ <VolunteerSection volunteer={volunteer} />
52
+ <AwardsSection awards={awards} />
53
+ <PublicationsSection publications={publications} />
54
+ <LanguagesSection languages={languages} />
55
+ <InterestsSection interests={interests} />
56
+ <ReferencesSection references={references} />
57
+ </Layout>
58
+ );
59
+ }
60
+
61
+ export default Resume;
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { Section, DateRange } from '@resume/core';
3
+ import { SectionTitle } from './layout-styles.jsx';
4
+ import {
5
+ EducationItem,
6
+ Institution,
7
+ Degree,
8
+ EducationDate,
9
+ } from './section-styles.jsx';
10
+
11
+ export function EducationSection({ education = [] }) {
12
+ if (!education?.length) return null;
13
+
14
+ return (
15
+ <Section>
16
+ <SectionTitle>Education</SectionTitle>
17
+ {education.map((edu, index) => (
18
+ <EducationItem key={index}>
19
+ <Institution>{edu.institution}</Institution>
20
+ <Degree>
21
+ {edu.studyType} in {edu.area}
22
+ {edu.score && ` • ${edu.score}`}
23
+ </Degree>
24
+ <EducationDate>
25
+ <DateRange startDate={edu.startDate} endDate={edu.endDate} />
26
+ </EducationDate>
27
+ </EducationItem>
28
+ ))}
29
+ </Section>
30
+ );
31
+ }
@@ -0,0 +1,168 @@
1
+ import React from 'react';
2
+ import { Section, DateRange } from '@resume/core';
3
+ import { SectionTitle } from './layout-styles.jsx';
4
+ import {
5
+ WorkItem,
6
+ WorkHeader,
7
+ Position,
8
+ Company,
9
+ DateText,
10
+ WorkSummary,
11
+ Highlights,
12
+ EducationItem,
13
+ Institution,
14
+ Degree,
15
+ EducationDate,
16
+ SkillsGrid,
17
+ SkillCategory,
18
+ SkillName,
19
+ SkillTags,
20
+ } from './section-styles.jsx';
21
+
22
+ export function ProjectsSection({ projects = [] }) {
23
+ if (!projects?.length) return null;
24
+
25
+ return (
26
+ <Section>
27
+ <SectionTitle>Projects</SectionTitle>
28
+ {projects.map((project, index) => (
29
+ <WorkItem key={index}>
30
+ <Position>{project.name}</Position>
31
+ {project.description && (
32
+ <WorkSummary>{project.description}</WorkSummary>
33
+ )}
34
+ {project.highlights?.length > 0 && (
35
+ <Highlights>
36
+ {project.highlights.map((highlight, i) => (
37
+ <li key={i}>{highlight}</li>
38
+ ))}
39
+ </Highlights>
40
+ )}
41
+ </WorkItem>
42
+ ))}
43
+ </Section>
44
+ );
45
+ }
46
+
47
+ export function VolunteerSection({ volunteer = [] }) {
48
+ if (!volunteer?.length) return null;
49
+
50
+ return (
51
+ <Section>
52
+ <SectionTitle>Volunteer</SectionTitle>
53
+ {volunteer.map((vol, index) => (
54
+ <WorkItem key={index}>
55
+ <WorkHeader>
56
+ <div>
57
+ <Position>{vol.position}</Position>
58
+ {vol.organization && <Company>{vol.organization}</Company>}
59
+ </div>
60
+ {(vol.startDate || vol.endDate) && (
61
+ <DateText>
62
+ <DateRange startDate={vol.startDate} endDate={vol.endDate} />
63
+ </DateText>
64
+ )}
65
+ </WorkHeader>
66
+ {vol.summary && <WorkSummary>{vol.summary}</WorkSummary>}
67
+ {vol.highlights?.length > 0 && (
68
+ <Highlights>
69
+ {vol.highlights.map((highlight, i) => (
70
+ <li key={i}>{highlight}</li>
71
+ ))}
72
+ </Highlights>
73
+ )}
74
+ </WorkItem>
75
+ ))}
76
+ </Section>
77
+ );
78
+ }
79
+
80
+ export function AwardsSection({ awards = [] }) {
81
+ if (!awards?.length) return null;
82
+
83
+ return (
84
+ <Section>
85
+ <SectionTitle>Awards</SectionTitle>
86
+ {awards.map((award, index) => (
87
+ <EducationItem key={index}>
88
+ <Institution>{award.title}</Institution>
89
+ {award.awarder && <Degree>Awarded by {award.awarder}</Degree>}
90
+ {award.date && <EducationDate>{award.date}</EducationDate>}
91
+ {award.summary && <WorkSummary>{award.summary}</WorkSummary>}
92
+ </EducationItem>
93
+ ))}
94
+ </Section>
95
+ );
96
+ }
97
+
98
+ export function PublicationsSection({ publications = [] }) {
99
+ if (!publications?.length) return null;
100
+
101
+ return (
102
+ <Section>
103
+ <SectionTitle>Publications</SectionTitle>
104
+ {publications.map((pub, index) => (
105
+ <EducationItem key={index}>
106
+ <Institution>{pub.name}</Institution>
107
+ {pub.publisher && <Degree>Published by {pub.publisher}</Degree>}
108
+ {pub.releaseDate && <EducationDate>{pub.releaseDate}</EducationDate>}
109
+ {pub.summary && <WorkSummary>{pub.summary}</WorkSummary>}
110
+ </EducationItem>
111
+ ))}
112
+ </Section>
113
+ );
114
+ }
115
+
116
+ export function LanguagesSection({ languages = [] }) {
117
+ if (!languages?.length) return null;
118
+
119
+ return (
120
+ <Section>
121
+ <SectionTitle>Languages</SectionTitle>
122
+ <SkillsGrid>
123
+ {languages.map((lang, index) => (
124
+ <SkillCategory key={index}>
125
+ <SkillName>{lang.language}</SkillName>
126
+ {lang.fluency && <SkillTags>{lang.fluency}</SkillTags>}
127
+ </SkillCategory>
128
+ ))}
129
+ </SkillsGrid>
130
+ </Section>
131
+ );
132
+ }
133
+
134
+ export function InterestsSection({ interests = [] }) {
135
+ if (!interests?.length) return null;
136
+
137
+ return (
138
+ <Section>
139
+ <SectionTitle>Interests</SectionTitle>
140
+ <SkillsGrid>
141
+ {interests.map((interest, index) => (
142
+ <SkillCategory key={index}>
143
+ <SkillName>{interest.name}</SkillName>
144
+ {interest.keywords?.length > 0 && (
145
+ <SkillTags>{interest.keywords.join(', ')}</SkillTags>
146
+ )}
147
+ </SkillCategory>
148
+ ))}
149
+ </SkillsGrid>
150
+ </Section>
151
+ );
152
+ }
153
+
154
+ export function ReferencesSection({ references = [] }) {
155
+ if (!references?.length) return null;
156
+
157
+ return (
158
+ <Section>
159
+ <SectionTitle>References</SectionTitle>
160
+ {references.map((ref, index) => (
161
+ <EducationItem key={index}>
162
+ <Institution>{ref.name}</Institution>
163
+ {ref.reference && <WorkSummary>{ref.reference}</WorkSummary>}
164
+ </EducationItem>
165
+ ))}
166
+ </Section>
167
+ );
168
+ }
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import { Section } from '@resume/core';
3
+ import { SectionTitle } from './layout-styles.jsx';
4
+ import {
5
+ SkillsGrid,
6
+ SkillCategory,
7
+ SkillName,
8
+ SkillTags,
9
+ } from './section-styles.jsx';
10
+
11
+ export function SkillsSection({ skills = [] }) {
12
+ if (!skills?.length) return null;
13
+
14
+ return (
15
+ <Section>
16
+ <SectionTitle>Skills</SectionTitle>
17
+ <SkillsGrid>
18
+ {skills.map((skill, index) => (
19
+ <SkillCategory key={index}>
20
+ <SkillName>{skill.name}</SkillName>
21
+ {skill.keywords?.length > 0 && (
22
+ <SkillTags>{skill.keywords.join(', ')}</SkillTags>
23
+ )}
24
+ </SkillCategory>
25
+ ))}
26
+ </SkillsGrid>
27
+ </Section>
28
+ );
29
+ }
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import { Section, DateRange } from '@resume/core';
3
+ import { SectionTitle } from './layout-styles.jsx';
4
+ import {
5
+ WorkItem,
6
+ WorkHeader,
7
+ Position,
8
+ Company,
9
+ DateText,
10
+ WorkSummary,
11
+ Highlights,
12
+ } from './section-styles.jsx';
13
+
14
+ export function WorkSection({ work = [] }) {
15
+ if (!work?.length) return null;
16
+
17
+ return (
18
+ <Section>
19
+ <SectionTitle>Experience</SectionTitle>
20
+ {work.map((job, index) => (
21
+ <WorkItem key={index}>
22
+ <WorkHeader>
23
+ <div>
24
+ <Position>{job.position}</Position>
25
+ {job.name && <Company>{job.name}</Company>}
26
+ </div>
27
+ <DateText>
28
+ <DateRange startDate={job.startDate} endDate={job.endDate} />
29
+ </DateText>
30
+ </WorkHeader>
31
+ {job.summary && <WorkSummary>{job.summary}</WorkSummary>}
32
+ {job.highlights?.length > 0 && (
33
+ <Highlights>
34
+ {job.highlights.map((highlight, i) => (
35
+ <li key={i}>{highlight}</li>
36
+ ))}
37
+ </Highlights>
38
+ )}
39
+ </WorkItem>
40
+ ))}
41
+ </Section>
42
+ );
43
+ }
@@ -0,0 +1,87 @@
1
+ import styled from 'styled-components';
2
+ import { ContactInfo } from '@resume/core';
3
+
4
+ export const Layout = styled.div`
5
+ max-width: 900px;
6
+ margin: 0 auto;
7
+ padding: 72px 48px;
8
+ background: #fafaf9;
9
+ font-family: 'Jost', 'Red Hat Display', -apple-system, BlinkMacSystemFont,
10
+ sans-serif;
11
+ color: #1f2937;
12
+ line-height: 1.6;
13
+
14
+ @media print {
15
+ padding: 40px;
16
+ background: white;
17
+ }
18
+ `;
19
+
20
+ export const Header = styled.header`
21
+ margin-bottom: 56px;
22
+ padding-bottom: 32px;
23
+ border-bottom: 1px solid #e5e7eb;
24
+ `;
25
+
26
+ export const Name = styled.h1`
27
+ font-size: 48px;
28
+ font-weight: 300;
29
+ color: #111827;
30
+ margin: 0 0 12px 0;
31
+ letter-spacing: 0.5px;
32
+ text-transform: uppercase;
33
+ `;
34
+
35
+ export const Label = styled.div`
36
+ font-size: 13px;
37
+ color: #6b7280;
38
+ margin-bottom: 24px;
39
+ font-weight: 400;
40
+ letter-spacing: 2px;
41
+ text-transform: uppercase;
42
+ `;
43
+
44
+ export const StyledContactInfo = styled(ContactInfo)`
45
+ font-size: 14px;
46
+ color: #6b7280;
47
+
48
+ a {
49
+ font-size: 14px;
50
+ color: #6b7280;
51
+ text-decoration: none;
52
+ border-bottom: 1px solid transparent;
53
+ transition: border-color 0.2s;
54
+
55
+ &:hover {
56
+ border-bottom-color: #6b7280;
57
+ }
58
+ }
59
+ `;
60
+
61
+ export const Summary = styled.p`
62
+ font-size: 15px;
63
+ line-height: 1.8;
64
+ color: #374151;
65
+ margin: 24px 0 0 0;
66
+ font-weight: 300;
67
+ `;
68
+
69
+ export const SectionTitle = styled.h2`
70
+ font-size: 12px;
71
+ font-weight: 400;
72
+ color: #6b7280;
73
+ margin: 48px 0 24px 0;
74
+ letter-spacing: 3px;
75
+ text-transform: uppercase;
76
+ position: relative;
77
+
78
+ &::after {
79
+ content: '';
80
+ position: absolute;
81
+ bottom: -8px;
82
+ left: 0;
83
+ right: 0;
84
+ height: 1px;
85
+ background: #e5e7eb;
86
+ }
87
+ `;
@@ -0,0 +1,135 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const WorkItem = styled.div`
4
+ padding: 24px 0;
5
+ border-bottom: 1px solid #e5e7eb;
6
+
7
+ &:last-child {
8
+ border-bottom: none;
9
+ }
10
+ `;
11
+
12
+ export const WorkHeader = styled.div`
13
+ display: grid;
14
+ grid-template-columns: 1fr auto;
15
+ gap: 24px;
16
+ margin-bottom: 12px;
17
+ align-items: baseline;
18
+
19
+ @media (max-width: 640px) {
20
+ grid-template-columns: 1fr;
21
+ gap: 8px;
22
+ }
23
+ `;
24
+
25
+ export const Position = styled.h3`
26
+ font-size: 18px;
27
+ font-weight: 400;
28
+ color: #111827;
29
+ margin: 0;
30
+ letter-spacing: 0.5px;
31
+ `;
32
+
33
+ export const Company = styled.div`
34
+ font-size: 15px;
35
+ color: #6b7280;
36
+ font-weight: 300;
37
+ margin-top: 4px;
38
+ `;
39
+
40
+ export const DateText = styled.div`
41
+ font-size: 13px;
42
+ color: #9ca3af;
43
+ font-weight: 300;
44
+ white-space: nowrap;
45
+ letter-spacing: 0.5px;
46
+ `;
47
+
48
+ export const WorkSummary = styled.p`
49
+ margin: 12px 0;
50
+ color: #4b5563;
51
+ line-height: 1.8;
52
+ font-size: 14px;
53
+ font-weight: 300;
54
+ `;
55
+
56
+ export const Highlights = styled.ul`
57
+ margin: 12px 0 0 0;
58
+ padding-left: 20px;
59
+ list-style-type: none;
60
+
61
+ li {
62
+ margin: 8px 0;
63
+ color: #4b5563;
64
+ line-height: 1.8;
65
+ font-size: 14px;
66
+ font-weight: 300;
67
+ position: relative;
68
+
69
+ &::before {
70
+ content: '—';
71
+ position: absolute;
72
+ left: -20px;
73
+ color: #6b7280;
74
+ }
75
+ }
76
+ `;
77
+
78
+ export const EducationItem = styled.div`
79
+ padding: 20px 0;
80
+ border-bottom: 1px solid #e5e7eb;
81
+
82
+ &:last-child {
83
+ border-bottom: none;
84
+ }
85
+ `;
86
+
87
+ export const Institution = styled.h3`
88
+ font-size: 16px;
89
+ font-weight: 400;
90
+ color: #111827;
91
+ margin: 0 0 8px 0;
92
+ letter-spacing: 0.5px;
93
+ `;
94
+
95
+ export const Degree = styled.div`
96
+ font-size: 14px;
97
+ color: #4b5563;
98
+ margin-bottom: 6px;
99
+ font-weight: 300;
100
+ `;
101
+
102
+ export const EducationDate = styled.div`
103
+ font-size: 13px;
104
+ color: #9ca3af;
105
+ font-weight: 300;
106
+ `;
107
+
108
+ export const SkillsGrid = styled.div`
109
+ display: grid;
110
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
111
+ gap: 1px;
112
+ background: #e5e7eb;
113
+ border: 1px solid #e5e7eb;
114
+ `;
115
+
116
+ export const SkillCategory = styled.div`
117
+ padding: 20px;
118
+ background: #fafaf9;
119
+ `;
120
+
121
+ export const SkillName = styled.h4`
122
+ font-size: 13px;
123
+ font-weight: 400;
124
+ color: #6b7280;
125
+ margin: 0 0 8px 0;
126
+ letter-spacing: 2px;
127
+ text-transform: uppercase;
128
+ `;
129
+
130
+ export const SkillTags = styled.div`
131
+ font-size: 14px;
132
+ color: #4b5563;
133
+ line-height: 1.6;
134
+ font-weight: 300;
135
+ `;
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=Jost:wght@300;400;500&family=Red+Hat+Display:wght@300;400;500&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
+ }