@jsonresume/jsonresume-theme-creative-studio 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/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # Creative Studio - JSON Resume Theme
2
+
3
+ **Artistic yet professional** resume theme inspired by Behance portfolios. Features clean typography, soft gradient section headers, and creative energy restrained just enough for corporate use while maintaining ATS compatibility.
4
+
5
+ ## Features
6
+
7
+ - **Rounded Sans-Serif Typography**: Uses Nunito and Poppins for a friendly, modern look
8
+ - **Warm Coral Accent**: Signature #ff6363 color for headers, links, and highlights
9
+ - **Soft Background Blocks**: Light #fff5f5 sections create subtle visual hierarchy
10
+ - **Generous Line Height**: 1.9 line-height for excellent readability
11
+ - **Gradient Hero Section**: Soft gradient background in the header for visual interest
12
+ - **Tag-Style Keywords**: Rounded pill-shaped tags for skills, projects, and interests
13
+ - **Professional Layout**: Single column, 800px max-width, optimized for printing
14
+
15
+ ## Design Philosophy
16
+
17
+ Creative Studio strikes the perfect balance between artistic flair and professional presentation. It's designed for creative professionals who want to showcase their personality without sacrificing ATS compatibility or corporate acceptance.
18
+
19
+ ## Color Palette
20
+
21
+ - **Accent**: #ff6363 (warm coral) - headers, links, tags
22
+ - **Background**: #fff5f5 (soft pink) - section backgrounds
23
+ - **Text**: #333 (dark gray) - primary text
24
+ - **Secondary**: #555, #666, #999 - hierarchical text levels
25
+
26
+ ## Typography
27
+
28
+ - **Headings**: Poppins (weights: 400, 600, 700)
29
+ - **Body**: Nunito (weights: 400, 600, 700)
30
+ - **Line Height**: 1.9 for exceptional readability
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ npm install @jsonresume/jsonresume-theme-creative-studio
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ### With resume-cli
41
+
42
+ ```bash
43
+ resume export resume.html --theme creative-studio
44
+ ```
45
+
46
+ ### Programmatically
47
+
48
+ ```javascript
49
+ const { render } = require('@jsonresume/jsonresume-theme-creative-studio');
50
+ const resume = require('./resume.json');
51
+
52
+ const html = render(resume);
53
+ ```
54
+
55
+ ## Sections Supported
56
+
57
+ - ✅ Basics (name, label, contact, profiles)
58
+ - ✅ Summary
59
+ - ✅ Work Experience
60
+ - ✅ Projects (with keyword tags)
61
+ - ✅ Education
62
+ - ✅ Certificates
63
+ - ✅ Publications
64
+ - ✅ Awards
65
+ - ✅ Volunteer
66
+ - ✅ Languages
67
+ - ✅ Skills (with keyword tags)
68
+ - ✅ Interests (with keyword tags)
69
+ - ✅ References
70
+
71
+ ## License
72
+
73
+ MIT
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "private": false,
3
+ "name": "@jsonresume/jsonresume-theme-creative-studio",
4
+ "version": "1.0.0",
5
+ "main": "./src/index.js",
6
+ "license": "MIT",
7
+ "description": "Creative Studio theme - artistic yet professional with warm coral accents and rounded typography",
8
+ "devDependencies": {
9
+ "react": "^18",
10
+ "styled-components": "^6",
11
+ "@repo/eslint-config-custom": "^0.0.0"
12
+ },
13
+ "peerDependencies": {
14
+ "styled-components": "^6"
15
+ },
16
+ "dependencies": {
17
+ "marked": "^16.3.0",
18
+ "prismjs": "^1.29.0",
19
+ "react-icons": "^5.0.1"
20
+ },
21
+ "scripts": {
22
+ "lint": "eslint ."
23
+ }
24
+ }
package/src/index.js ADDED
@@ -0,0 +1,84 @@
1
+ import { renderToString } from 'react-dom/server';
2
+ import { ServerStyleSheet } from 'styled-components';
3
+ import Resume from './ui/Resume';
4
+
5
+ export const 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=Nunito:wght@400;600;700&family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
16
+ <style>
17
+ html {
18
+ font-family: 'Nunito', 'Poppins', sans-serif;
19
+ background: #fff;
20
+ font-size: 16px;
21
+ color: #333;
22
+ }
23
+
24
+ body {
25
+ margin: 0;
26
+ padding: 0;
27
+ }
28
+
29
+ h1, h2, h3, h4, h5, h6 {
30
+ font-family: 'Poppins', sans-serif;
31
+ margin: 0;
32
+ padding: 0;
33
+ }
34
+
35
+ h2 {
36
+ font-size: 1.75rem;
37
+ font-weight: 700;
38
+ color: #ff6363;
39
+ margin-bottom: 1rem;
40
+ }
41
+
42
+ h3 {
43
+ font-size: 1.25rem;
44
+ font-weight: 600;
45
+ color: #333;
46
+ }
47
+
48
+ p {
49
+ padding: 0;
50
+ margin: 0;
51
+ line-height: 1.9;
52
+ }
53
+
54
+ p, li {
55
+ font-size: 1rem;
56
+ line-height: 1.9;
57
+ }
58
+
59
+ a {
60
+ color: #ff6363;
61
+ text-decoration: none;
62
+ transition: color 0.2s ease;
63
+ }
64
+
65
+ a:hover {
66
+ color: #ff4545;
67
+ }
68
+
69
+ ul {
70
+ list-style: none;
71
+ margin: 0;
72
+ padding: 0;
73
+ }
74
+
75
+ *,
76
+ *::before,
77
+ *::after {
78
+ box-sizing: border-box;
79
+ }
80
+ </style>
81
+ ${styles}</head><body>${html}</body></html>`;
82
+ };
83
+
84
+ export { Resume };
@@ -0,0 +1,59 @@
1
+ import styled from 'styled-components';
2
+ import Section from './Section';
3
+ import Date from './Date';
4
+
5
+ const AwardItem = styled.div`
6
+ margin-bottom: 1.5rem;
7
+
8
+ &:last-child {
9
+ margin-bottom: 0;
10
+ }
11
+ `;
12
+
13
+ const AwardTitle = styled.h3`
14
+ font-size: 1.1rem;
15
+ font-weight: 600;
16
+ color: #333;
17
+ margin-bottom: 0.25rem;
18
+ `;
19
+
20
+ const Awarder = styled.p`
21
+ color: #ff6363;
22
+ font-weight: 600;
23
+ font-size: 0.95rem;
24
+ margin-bottom: 0.25rem;
25
+ `;
26
+
27
+ const AwardDate = styled.p`
28
+ color: #999;
29
+ font-size: 0.9rem;
30
+ font-style: italic;
31
+ `;
32
+
33
+ const Summary = styled.p`
34
+ margin-top: 0.5rem;
35
+ color: #555;
36
+ `;
37
+
38
+ const Awards = ({ awards }) => {
39
+ if (!awards || awards.length === 0) return null;
40
+
41
+ return (
42
+ <Section title="Awards">
43
+ {awards.map((award, i) => (
44
+ <AwardItem key={i}>
45
+ <AwardTitle>{award.title}</AwardTitle>
46
+ {award.awarder && <Awarder>{award.awarder}</Awarder>}
47
+ {award.date && (
48
+ <AwardDate>
49
+ <Date date={award.date} />
50
+ </AwardDate>
51
+ )}
52
+ {award.summary && <Summary>{award.summary}</Summary>}
53
+ </AwardItem>
54
+ ))}
55
+ </Section>
56
+ );
57
+ };
58
+
59
+ export default Awards;
@@ -0,0 +1,66 @@
1
+ import styled from 'styled-components';
2
+ import Section from './Section';
3
+ import Date from './Date';
4
+
5
+ const CertItem = styled.div`
6
+ margin-bottom: 1.5rem;
7
+
8
+ &:last-child {
9
+ margin-bottom: 0;
10
+ }
11
+ `;
12
+
13
+ const CertName = styled.h3`
14
+ font-size: 1.1rem;
15
+ font-weight: 600;
16
+ color: #333;
17
+ margin-bottom: 0.25rem;
18
+ `;
19
+
20
+ const Issuer = styled.p`
21
+ color: #ff6363;
22
+ font-weight: 600;
23
+ font-size: 0.95rem;
24
+ margin-bottom: 0.25rem;
25
+ `;
26
+
27
+ const CertDate = styled.p`
28
+ color: #999;
29
+ font-size: 0.9rem;
30
+ font-style: italic;
31
+ `;
32
+
33
+ const CertLink = styled.a`
34
+ display: inline-block;
35
+ margin-top: 0.5rem;
36
+ color: #ff6363;
37
+ font-weight: 600;
38
+ font-size: 0.9rem;
39
+ `;
40
+
41
+ const Certificates = ({ certificates }) => {
42
+ if (!certificates || certificates.length === 0) return null;
43
+
44
+ return (
45
+ <Section title="Certificates">
46
+ {certificates.map((cert, i) => (
47
+ <CertItem key={i}>
48
+ <CertName>{cert.name}</CertName>
49
+ {cert.issuer && <Issuer>{cert.issuer}</Issuer>}
50
+ {cert.date && (
51
+ <CertDate>
52
+ <Date date={cert.date} />
53
+ </CertDate>
54
+ )}
55
+ {cert.url && (
56
+ <CertLink href={cert.url} target="_blank" rel="noopener noreferrer">
57
+ View Certificate →
58
+ </CertLink>
59
+ )}
60
+ </CertItem>
61
+ ))}
62
+ </Section>
63
+ );
64
+ };
65
+
66
+ export default Certificates;
package/src/ui/Date.js ADDED
@@ -0,0 +1,11 @@
1
+ const Date = ({ date }) => {
2
+ if (!date) return null;
3
+
4
+ const dateObj = new window.Date(date);
5
+ const month = dateObj.toLocaleString('default', { month: 'short' });
6
+ const year = dateObj.getFullYear();
7
+
8
+ return `${month} ${year}`;
9
+ };
10
+
11
+ export default Date;
@@ -0,0 +1,21 @@
1
+ import styled from 'styled-components';
2
+ import Date from './Date';
3
+
4
+ const DateRangeText = styled.p`
5
+ color: #999;
6
+ font-size: 0.95rem;
7
+ font-style: italic;
8
+ `;
9
+
10
+ const DateRange = ({ startDate, endDate }) => {
11
+ if (!startDate) return null;
12
+
13
+ return (
14
+ <DateRangeText>
15
+ <Date date={startDate} /> -{' '}
16
+ {endDate ? <Date date={endDate} /> : 'Present'}
17
+ </DateRangeText>
18
+ );
19
+ };
20
+
21
+ export default DateRange;
@@ -0,0 +1,69 @@
1
+ import styled from 'styled-components';
2
+ import Section from './Section';
3
+ import DateRange from './DateRange';
4
+
5
+ const EducationItem = styled.div`
6
+ margin-bottom: 2rem;
7
+
8
+ &:last-child {
9
+ margin-bottom: 0;
10
+ }
11
+ `;
12
+
13
+ const Degree = styled.h3`
14
+ font-size: 1.25rem;
15
+ font-weight: 600;
16
+ color: #333;
17
+ margin-bottom: 0.25rem;
18
+ `;
19
+
20
+ const Institution = styled.p`
21
+ font-size: 1.1rem;
22
+ color: #ff6363;
23
+ font-weight: 600;
24
+ margin-bottom: 0.5rem;
25
+ `;
26
+
27
+ const Area = styled.p`
28
+ color: #666;
29
+ margin-top: 0.25rem;
30
+ `;
31
+
32
+ const Courses = styled.ul`
33
+ margin-top: 0.75rem;
34
+ padding-left: 1.5rem;
35
+ list-style: disc;
36
+
37
+ li {
38
+ color: #555;
39
+ margin-bottom: 0.5rem;
40
+ }
41
+ `;
42
+
43
+ const Education = ({ education }) => {
44
+ if (!education || education.length === 0) return null;
45
+
46
+ return (
47
+ <Section title="Education">
48
+ {education.map((item, i) => (
49
+ <EducationItem key={i}>
50
+ <Degree>
51
+ {item.studyType} {item.area && `in ${item.area}`}
52
+ </Degree>
53
+ <Institution>{item.institution}</Institution>
54
+ <DateRange startDate={item.startDate} endDate={item.endDate} />
55
+ {item.score && <Area>GPA: {item.score}</Area>}
56
+ {item.courses && item.courses.length > 0 && (
57
+ <Courses>
58
+ {item.courses.map((course, j) => (
59
+ <li key={j}>{course}</li>
60
+ ))}
61
+ </Courses>
62
+ )}
63
+ </EducationItem>
64
+ ))}
65
+ </Section>
66
+ );
67
+ };
68
+
69
+ export default Education;
package/src/ui/Hero.js ADDED
@@ -0,0 +1,115 @@
1
+ import styled from 'styled-components';
2
+ import { FaEnvelope, FaPhone, FaGlobe, FaMapMarkerAlt } from 'react-icons/fa';
3
+
4
+ const HeroContainer = styled.div`
5
+ background: linear-gradient(135deg, #fff5f5 0%, #ffe8e8 100%);
6
+ padding: 40px;
7
+ border-radius: 12px;
8
+ margin-bottom: 40px;
9
+ text-align: center;
10
+ `;
11
+
12
+ const Name = styled.h1`
13
+ font-size: 3rem;
14
+ font-weight: 700;
15
+ color: #333;
16
+ margin-bottom: 0.5rem;
17
+ line-height: 1.2;
18
+ `;
19
+
20
+ const Label = styled.p`
21
+ font-size: 1.25rem;
22
+ color: #ff6363;
23
+ font-weight: 600;
24
+ margin-bottom: 1.5rem;
25
+ `;
26
+
27
+ const ContactInfo = styled.div`
28
+ display: flex;
29
+ flex-wrap: wrap;
30
+ justify-content: center;
31
+ gap: 20px;
32
+ margin-top: 20px;
33
+ `;
34
+
35
+ const ContactItem = styled.a`
36
+ display: flex;
37
+ align-items: center;
38
+ gap: 8px;
39
+ color: #666;
40
+ font-size: 0.95rem;
41
+
42
+ svg {
43
+ color: #ff6363;
44
+ }
45
+ `;
46
+
47
+ const Profiles = styled.div`
48
+ display: flex;
49
+ flex-wrap: wrap;
50
+ justify-content: center;
51
+ gap: 15px;
52
+ margin-top: 20px;
53
+ `;
54
+
55
+ const ProfileLink = styled.a`
56
+ color: #ff6363;
57
+ font-weight: 600;
58
+ font-size: 0.95rem;
59
+ `;
60
+
61
+ const Hero = ({ basics }) => {
62
+ if (!basics) return null;
63
+
64
+ return (
65
+ <HeroContainer>
66
+ <Name>{basics.name}</Name>
67
+ {basics.label && <Label>{basics.label}</Label>}
68
+
69
+ <ContactInfo>
70
+ {basics.email && (
71
+ <ContactItem href={`mailto:${basics.email}`}>
72
+ <FaEnvelope /> {basics.email}
73
+ </ContactItem>
74
+ )}
75
+ {basics.phone && (
76
+ <ContactItem href={`tel:${basics.phone}`}>
77
+ <FaPhone /> {basics.phone}
78
+ </ContactItem>
79
+ )}
80
+ {basics.url && (
81
+ <ContactItem
82
+ href={basics.url}
83
+ target="_blank"
84
+ rel="noopener noreferrer"
85
+ >
86
+ <FaGlobe /> {basics.url.replace(/^https?:\/\//, '')}
87
+ </ContactItem>
88
+ )}
89
+ {basics.location?.city && (
90
+ <ContactItem as="span">
91
+ <FaMapMarkerAlt /> {basics.location.city}
92
+ {basics.location.region && `, ${basics.location.region}`}
93
+ </ContactItem>
94
+ )}
95
+ </ContactInfo>
96
+
97
+ {basics.profiles && basics.profiles.length > 0 && (
98
+ <Profiles>
99
+ {basics.profiles.map((profile, i) => (
100
+ <ProfileLink
101
+ key={i}
102
+ href={profile.url}
103
+ target="_blank"
104
+ rel="noopener noreferrer"
105
+ >
106
+ {profile.network}
107
+ </ProfileLink>
108
+ ))}
109
+ </Profiles>
110
+ )}
111
+ </HeroContainer>
112
+ );
113
+ };
114
+
115
+ export default Hero;
@@ -0,0 +1,60 @@
1
+ import styled from 'styled-components';
2
+ import Section from './Section';
3
+
4
+ const InterestList = styled.div`
5
+ display: flex;
6
+ flex-wrap: wrap;
7
+ gap: 1rem;
8
+ `;
9
+
10
+ const InterestItem = styled.div`
11
+ background: #ffe8e8;
12
+ padding: 15px 20px;
13
+ border-radius: 20px;
14
+ `;
15
+
16
+ const InterestName = styled.h3`
17
+ font-size: 1rem;
18
+ font-weight: 600;
19
+ color: #333;
20
+ margin-bottom: 0.5rem;
21
+ `;
22
+
23
+ const Keywords = styled.div`
24
+ display: flex;
25
+ flex-wrap: wrap;
26
+ gap: 6px;
27
+ `;
28
+
29
+ const Keyword = styled.span`
30
+ background: #fff;
31
+ color: #ff6363;
32
+ padding: 3px 10px;
33
+ border-radius: 12px;
34
+ font-size: 0.85rem;
35
+ `;
36
+
37
+ const Interests = ({ interests }) => {
38
+ if (!interests || interests.length === 0) return null;
39
+
40
+ return (
41
+ <Section title="Interests">
42
+ <InterestList>
43
+ {interests.map((interest, i) => (
44
+ <InterestItem key={i}>
45
+ <InterestName>{interest.name}</InterestName>
46
+ {interest.keywords && interest.keywords.length > 0 && (
47
+ <Keywords>
48
+ {interest.keywords.map((keyword, j) => (
49
+ <Keyword key={j}>{keyword}</Keyword>
50
+ ))}
51
+ </Keywords>
52
+ )}
53
+ </InterestItem>
54
+ ))}
55
+ </InterestList>
56
+ </Section>
57
+ );
58
+ };
59
+
60
+ export default Interests;
@@ -0,0 +1,46 @@
1
+ import styled from 'styled-components';
2
+ import Section from './Section';
3
+
4
+ const LanguageList = styled.div`
5
+ display: grid;
6
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
7
+ gap: 1rem;
8
+ `;
9
+
10
+ const LanguageItem = styled.div`
11
+ background: #ffe8e8;
12
+ padding: 15px;
13
+ border-radius: 8px;
14
+ `;
15
+
16
+ const LanguageName = styled.h3`
17
+ font-size: 1.1rem;
18
+ font-weight: 600;
19
+ color: #333;
20
+ margin-bottom: 0.25rem;
21
+ `;
22
+
23
+ const Fluency = styled.p`
24
+ color: #ff6363;
25
+ font-weight: 600;
26
+ font-size: 0.9rem;
27
+ `;
28
+
29
+ const Languages = ({ languages }) => {
30
+ if (!languages || languages.length === 0) return null;
31
+
32
+ return (
33
+ <Section title="Languages">
34
+ <LanguageList>
35
+ {languages.map((lang, i) => (
36
+ <LanguageItem key={i}>
37
+ <LanguageName>{lang.language}</LanguageName>
38
+ {lang.fluency && <Fluency>{lang.fluency}</Fluency>}
39
+ </LanguageItem>
40
+ ))}
41
+ </LanguageList>
42
+ </Section>
43
+ );
44
+ };
45
+
46
+ export default Languages;
@@ -0,0 +1,84 @@
1
+ import styled from 'styled-components';
2
+ import Section from './Section';
3
+ import DateRange from './DateRange';
4
+
5
+ const ProjectItem = styled.div`
6
+ margin-bottom: 2rem;
7
+
8
+ &:last-child {
9
+ margin-bottom: 0;
10
+ }
11
+ `;
12
+
13
+ const ProjectName = styled.h3`
14
+ font-size: 1.25rem;
15
+ font-weight: 600;
16
+ color: #333;
17
+ margin-bottom: 0.25rem;
18
+ `;
19
+
20
+ const ProjectLink = styled.a`
21
+ color: #ff6363;
22
+ font-weight: 600;
23
+ margin-left: 0.5rem;
24
+ font-size: 0.95rem;
25
+ `;
26
+
27
+ const Description = styled.p`
28
+ margin-top: 0.75rem;
29
+ color: #555;
30
+ `;
31
+
32
+ const Keywords = styled.div`
33
+ display: flex;
34
+ flex-wrap: wrap;
35
+ gap: 8px;
36
+ margin-top: 0.75rem;
37
+ `;
38
+
39
+ const Keyword = styled.span`
40
+ background: #ffe8e8;
41
+ color: #ff6363;
42
+ padding: 4px 12px;
43
+ border-radius: 16px;
44
+ font-size: 0.85rem;
45
+ font-weight: 600;
46
+ `;
47
+
48
+ const Projects = ({ projects }) => {
49
+ if (!projects || projects.length === 0) return null;
50
+
51
+ return (
52
+ <Section title="Projects">
53
+ {projects.map((project, i) => (
54
+ <ProjectItem key={i}>
55
+ <ProjectName>
56
+ {project.name}
57
+ {project.url && (
58
+ <ProjectLink
59
+ href={project.url}
60
+ target="_blank"
61
+ rel="noopener noreferrer"
62
+ >
63
+ View Project →
64
+ </ProjectLink>
65
+ )}
66
+ </ProjectName>
67
+ <DateRange startDate={project.startDate} endDate={project.endDate} />
68
+ {project.description && (
69
+ <Description>{project.description}</Description>
70
+ )}
71
+ {project.keywords && project.keywords.length > 0 && (
72
+ <Keywords>
73
+ {project.keywords.map((keyword, j) => (
74
+ <Keyword key={j}>{keyword}</Keyword>
75
+ ))}
76
+ </Keywords>
77
+ )}
78
+ </ProjectItem>
79
+ ))}
80
+ </Section>
81
+ );
82
+ };
83
+
84
+ export default Projects;
@@ -0,0 +1,72 @@
1
+ import styled from 'styled-components';
2
+ import Section from './Section';
3
+ import Date from './Date';
4
+
5
+ const PubItem = styled.div`
6
+ margin-bottom: 1.5rem;
7
+
8
+ &:last-child {
9
+ margin-bottom: 0;
10
+ }
11
+ `;
12
+
13
+ const PubName = styled.h3`
14
+ font-size: 1.1rem;
15
+ font-weight: 600;
16
+ color: #333;
17
+ margin-bottom: 0.25rem;
18
+ `;
19
+
20
+ const Publisher = styled.p`
21
+ color: #ff6363;
22
+ font-weight: 600;
23
+ font-size: 0.95rem;
24
+ margin-bottom: 0.25rem;
25
+ `;
26
+
27
+ const PubDate = styled.p`
28
+ color: #999;
29
+ font-size: 0.9rem;
30
+ font-style: italic;
31
+ `;
32
+
33
+ const Summary = styled.p`
34
+ margin-top: 0.5rem;
35
+ color: #555;
36
+ `;
37
+
38
+ const PubLink = styled.a`
39
+ display: inline-block;
40
+ margin-top: 0.5rem;
41
+ color: #ff6363;
42
+ font-weight: 600;
43
+ font-size: 0.9rem;
44
+ `;
45
+
46
+ const Publications = ({ publications }) => {
47
+ if (!publications || publications.length === 0) return null;
48
+
49
+ return (
50
+ <Section title="Publications">
51
+ {publications.map((pub, i) => (
52
+ <PubItem key={i}>
53
+ <PubName>{pub.name}</PubName>
54
+ {pub.publisher && <Publisher>{pub.publisher}</Publisher>}
55
+ {pub.releaseDate && (
56
+ <PubDate>
57
+ <Date date={pub.releaseDate} />
58
+ </PubDate>
59
+ )}
60
+ {pub.summary && <Summary>{pub.summary}</Summary>}
61
+ {pub.url && (
62
+ <PubLink href={pub.url} target="_blank" rel="noopener noreferrer">
63
+ Read Publication →
64
+ </PubLink>
65
+ )}
66
+ </PubItem>
67
+ ))}
68
+ </Section>
69
+ );
70
+ };
71
+
72
+ export default Publications;
@@ -0,0 +1,44 @@
1
+ import styled from 'styled-components';
2
+ import Section from './Section';
3
+
4
+ const ReferenceItem = styled.div`
5
+ margin-bottom: 1.5rem;
6
+ background: #ffe8e8;
7
+ padding: 20px;
8
+ border-radius: 8px;
9
+ border-left: 4px solid #ff6363;
10
+
11
+ &:last-child {
12
+ margin-bottom: 0;
13
+ }
14
+ `;
15
+
16
+ const RefName = styled.h3`
17
+ font-size: 1.1rem;
18
+ font-weight: 600;
19
+ color: #333;
20
+ margin-bottom: 0.5rem;
21
+ `;
22
+
23
+ const Quote = styled.p`
24
+ color: #555;
25
+ font-style: italic;
26
+ line-height: 1.9;
27
+ `;
28
+
29
+ const References = ({ references }) => {
30
+ if (!references || references.length === 0) return null;
31
+
32
+ return (
33
+ <Section title="References">
34
+ {references.map((ref, i) => (
35
+ <ReferenceItem key={i}>
36
+ <RefName>{ref.name}</RefName>
37
+ {ref.reference && <Quote>"{ref.reference}"</Quote>}
38
+ </ReferenceItem>
39
+ ))}
40
+ </Section>
41
+ );
42
+ };
43
+
44
+ export default References;
@@ -0,0 +1,43 @@
1
+ import styled from 'styled-components';
2
+ import Projects from './Projects';
3
+ import Hero from './Hero';
4
+ import Summary from './Summary';
5
+ import Education from './Education';
6
+ import Work from './Work';
7
+ import Certificates from './Certificates';
8
+ import Publications from './Publications';
9
+ import Awards from './Awards';
10
+ import Skills from './Skills';
11
+ import Interests from './Interests';
12
+ import Languages from './Languages';
13
+ import References from './References';
14
+ import Volunteer from './Volunteer';
15
+
16
+ const Layout = styled.div`
17
+ max-width: 800px;
18
+ margin: 0 auto;
19
+ padding: 40px 20px;
20
+ line-height: 1.9;
21
+ `;
22
+
23
+ const Resume = ({ resume }) => {
24
+ return (
25
+ <Layout>
26
+ <Hero basics={resume.basics} />
27
+ <Summary basics={resume.basics} />
28
+ <Work work={resume.work} />
29
+ <Projects projects={resume.projects} />
30
+ <Education education={resume.education} />
31
+ <Certificates certificates={resume.certificates} />
32
+ <Publications publications={resume.publications} />
33
+ <Awards awards={resume.awards} />
34
+ <Volunteer volunteer={resume.volunteer} />
35
+ <Languages languages={resume.languages} />
36
+ <Skills skills={resume.skills} />
37
+ <Interests interests={resume.interests} />
38
+ <References references={resume.references} />
39
+ </Layout>
40
+ );
41
+ };
42
+
43
+ export default Resume;
@@ -0,0 +1,30 @@
1
+ import styled from 'styled-components';
2
+
3
+ const SectionContainer = styled.section`
4
+ background: #fff5f5;
5
+ padding: 30px;
6
+ border-radius: 12px;
7
+ margin-bottom: 30px;
8
+ `;
9
+
10
+ const SectionTitle = styled.h2`
11
+ font-size: 1.75rem;
12
+ font-weight: 700;
13
+ color: #ff6363;
14
+ margin-bottom: 1.5rem;
15
+ padding-bottom: 0.5rem;
16
+ border-bottom: 2px solid #ff6363;
17
+ `;
18
+
19
+ const Section = ({ title, children }) => {
20
+ if (!children) return null;
21
+
22
+ return (
23
+ <SectionContainer>
24
+ <SectionTitle>{title}</SectionTitle>
25
+ {children}
26
+ </SectionContainer>
27
+ );
28
+ };
29
+
30
+ export default Section;
@@ -0,0 +1,55 @@
1
+ import styled from 'styled-components';
2
+ import Section from './Section';
3
+
4
+ const SkillCategory = styled.div`
5
+ margin-bottom: 1.5rem;
6
+
7
+ &:last-child {
8
+ margin-bottom: 0;
9
+ }
10
+ `;
11
+
12
+ const SkillName = styled.h3`
13
+ font-size: 1.1rem;
14
+ font-weight: 600;
15
+ color: #333;
16
+ margin-bottom: 0.5rem;
17
+ `;
18
+
19
+ const SkillList = styled.div`
20
+ display: flex;
21
+ flex-wrap: wrap;
22
+ gap: 8px;
23
+ `;
24
+
25
+ const SkillItem = styled.span`
26
+ background: #ffe8e8;
27
+ color: #ff6363;
28
+ padding: 6px 14px;
29
+ border-radius: 16px;
30
+ font-size: 0.9rem;
31
+ font-weight: 600;
32
+ `;
33
+
34
+ const Skills = ({ skills }) => {
35
+ if (!skills || skills.length === 0) return null;
36
+
37
+ return (
38
+ <Section title="Skills">
39
+ {skills.map((skill, i) => (
40
+ <SkillCategory key={i}>
41
+ <SkillName>{skill.name}</SkillName>
42
+ {skill.keywords && skill.keywords.length > 0 && (
43
+ <SkillList>
44
+ {skill.keywords.map((keyword, j) => (
45
+ <SkillItem key={j}>{keyword}</SkillItem>
46
+ ))}
47
+ </SkillList>
48
+ )}
49
+ </SkillCategory>
50
+ ))}
51
+ </Section>
52
+ );
53
+ };
54
+
55
+ export default Skills;
@@ -0,0 +1,20 @@
1
+ import styled from 'styled-components';
2
+ import Section from './Section';
3
+
4
+ const SummaryText = styled.p`
5
+ font-size: 1.1rem;
6
+ line-height: 1.9;
7
+ color: #555;
8
+ `;
9
+
10
+ const Summary = ({ basics }) => {
11
+ if (!basics?.summary) return null;
12
+
13
+ return (
14
+ <Section title="About">
15
+ <SummaryText>{basics.summary}</SummaryText>
16
+ </Section>
17
+ );
18
+ };
19
+
20
+ export default Summary;
@@ -0,0 +1,49 @@
1
+ import styled from 'styled-components';
2
+ import Section from './Section';
3
+ import DateRange from './DateRange';
4
+
5
+ const VolunteerItem = styled.div`
6
+ margin-bottom: 2rem;
7
+
8
+ &:last-child {
9
+ margin-bottom: 0;
10
+ }
11
+ `;
12
+
13
+ const Position = styled.h3`
14
+ font-size: 1.25rem;
15
+ font-weight: 600;
16
+ color: #333;
17
+ margin-bottom: 0.25rem;
18
+ `;
19
+
20
+ const Organization = styled.p`
21
+ font-size: 1.1rem;
22
+ color: #ff6363;
23
+ font-weight: 600;
24
+ margin-bottom: 0.5rem;
25
+ `;
26
+
27
+ const Description = styled.p`
28
+ margin-top: 0.75rem;
29
+ color: #555;
30
+ `;
31
+
32
+ const Volunteer = ({ volunteer }) => {
33
+ if (!volunteer || volunteer.length === 0) return null;
34
+
35
+ return (
36
+ <Section title="Volunteer">
37
+ {volunteer.map((item, i) => (
38
+ <VolunteerItem key={i}>
39
+ <Position>{item.position}</Position>
40
+ <Organization>{item.organization}</Organization>
41
+ <DateRange startDate={item.startDate} endDate={item.endDate} />
42
+ {item.summary && <Description>{item.summary}</Description>}
43
+ </VolunteerItem>
44
+ ))}
45
+ </Section>
46
+ );
47
+ };
48
+
49
+ export default Volunteer;
package/src/ui/Work.js ADDED
@@ -0,0 +1,67 @@
1
+ import styled from 'styled-components';
2
+ import Section from './Section';
3
+ import DateRange from './DateRange';
4
+
5
+ const WorkItem = styled.div`
6
+ margin-bottom: 2rem;
7
+
8
+ &:last-child {
9
+ margin-bottom: 0;
10
+ }
11
+ `;
12
+
13
+ const Position = styled.h3`
14
+ font-size: 1.25rem;
15
+ font-weight: 600;
16
+ color: #333;
17
+ margin-bottom: 0.25rem;
18
+ `;
19
+
20
+ const Company = styled.p`
21
+ font-size: 1.1rem;
22
+ color: #ff6363;
23
+ font-weight: 600;
24
+ margin-bottom: 0.5rem;
25
+ `;
26
+
27
+ const Description = styled.p`
28
+ margin-top: 0.75rem;
29
+ color: #555;
30
+ `;
31
+
32
+ const Highlights = styled.ul`
33
+ margin-top: 0.75rem;
34
+ padding-left: 1.5rem;
35
+ list-style: disc;
36
+
37
+ li {
38
+ color: #555;
39
+ margin-bottom: 0.5rem;
40
+ }
41
+ `;
42
+
43
+ const Work = ({ work }) => {
44
+ if (!work || work.length === 0) return null;
45
+
46
+ return (
47
+ <Section title="Experience">
48
+ {work.map((item, i) => (
49
+ <WorkItem key={i}>
50
+ <Position>{item.position}</Position>
51
+ <Company>{item.name}</Company>
52
+ <DateRange startDate={item.startDate} endDate={item.endDate} />
53
+ {item.summary && <Description>{item.summary}</Description>}
54
+ {item.highlights && item.highlights.length > 0 && (
55
+ <Highlights>
56
+ {item.highlights.map((highlight, j) => (
57
+ <li key={j}>{highlight}</li>
58
+ ))}
59
+ </Highlights>
60
+ )}
61
+ </WorkItem>
62
+ ))}
63
+ </Section>
64
+ );
65
+ };
66
+
67
+ export default Work;