jsonresume-theme-urban-techno 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 +456 -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=Roboto+Condensed:wght@400;700;900&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-urban-techno",
3
+ "version": "0.1.0",
4
+ "description": "Brutalist tech theme with high contrast, dense two-column layout, and bold typography",
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,456 @@
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: 1000px;
16
+ margin: 0 auto;
17
+ padding: 0;
18
+ background: white;
19
+ font-family: 'Roboto Condensed', -apple-system, BlinkMacSystemFont, sans-serif;
20
+ color: #111;
21
+ line-height: 1.4;
22
+ font-size: 13px;
23
+ border: 3px solid #111;
24
+
25
+ @media print {
26
+ border: none;
27
+ }
28
+ `;
29
+
30
+ const Header = styled.header`
31
+ display: grid;
32
+ grid-template-columns: 1fr 1fr;
33
+ border-bottom: 3px solid #111;
34
+ `;
35
+
36
+ const NameSection = styled.div`
37
+ padding: 24px;
38
+ border-right: 3px solid #111;
39
+ background: #111;
40
+ color: white;
41
+ `;
42
+
43
+ const Name = styled.h1`
44
+ font-size: 36px;
45
+ font-weight: 900;
46
+ margin: 0;
47
+ text-transform: uppercase;
48
+ letter-spacing: -1px;
49
+ line-height: 0.9;
50
+ `;
51
+
52
+ const Tagline = styled.div`
53
+ font-size: 11px;
54
+ margin: 8px 0 0 0;
55
+ font-weight: 700;
56
+ text-transform: uppercase;
57
+ letter-spacing: 1px;
58
+ color: #666;
59
+ `;
60
+
61
+ const ContactSection = styled.div`
62
+ padding: 24px;
63
+ background: white;
64
+ display: flex;
65
+ flex-direction: column;
66
+ gap: 12px;
67
+ justify-content: center;
68
+ `;
69
+
70
+ const ContactItem = styled.div`
71
+ font-size: 11px;
72
+ font-weight: 700;
73
+ text-transform: uppercase;
74
+ letter-spacing: 0.5px;
75
+
76
+ a {
77
+ color: #111;
78
+ text-decoration: none;
79
+ border-bottom: 1px solid #666;
80
+
81
+ &:hover {
82
+ border-bottom-color: #111;
83
+ }
84
+ }
85
+ `;
86
+
87
+ const MainGrid = styled.div`
88
+ display: grid;
89
+ grid-template-columns: 280px 1fr;
90
+ `;
91
+
92
+ const Sidebar = styled.aside`
93
+ border-right: 3px solid #111;
94
+ background: #f5f5f5;
95
+ `;
96
+
97
+ const MainContent = styled.main`
98
+ background: white;
99
+ `;
100
+
101
+ const SidebarSection = styled(Section)`
102
+ border-bottom: 1px solid #666;
103
+
104
+ &:last-child {
105
+ border-bottom: none;
106
+ }
107
+ `;
108
+
109
+ const SidebarSectionTitle = styled(SectionTitle)`
110
+ background: #111;
111
+ color: white;
112
+ font-size: 11px;
113
+ font-weight: 900;
114
+ margin: 0;
115
+ padding: 8px 16px;
116
+ text-transform: uppercase;
117
+ letter-spacing: 1px;
118
+ `;
119
+
120
+ const SidebarContent = styled.div`
121
+ padding: 16px;
122
+ `;
123
+
124
+ const SkillItem = styled.div`
125
+ margin-bottom: 12px;
126
+
127
+ &:last-child {
128
+ margin-bottom: 0;
129
+ }
130
+
131
+ h4 {
132
+ font-size: 10px;
133
+ font-weight: 900;
134
+ margin: 0 0 6px 0;
135
+ text-transform: uppercase;
136
+ letter-spacing: 0.5px;
137
+ }
138
+ `;
139
+
140
+ const StyledBadgeList = styled(BadgeList)`
141
+ display: flex;
142
+ flex-wrap: wrap;
143
+ gap: 4px;
144
+ `;
145
+
146
+ const StyledBadge = styled(Badge)`
147
+ font-size: 9px;
148
+ padding: 4px 8px;
149
+ background: white;
150
+ border: 1px solid #111;
151
+ color: #111;
152
+ font-weight: 700;
153
+ border-radius: 0;
154
+ text-transform: uppercase;
155
+ letter-spacing: 0.3px;
156
+ white-space: nowrap;
157
+ `;
158
+
159
+ const MainSection = styled(Section)`
160
+ border-bottom: 3px solid #111;
161
+
162
+ &:last-child {
163
+ border-bottom: none;
164
+ }
165
+ `;
166
+
167
+ const MainSectionTitle = styled(SectionTitle)`
168
+ background: #111;
169
+ color: white;
170
+ font-size: 14px;
171
+ font-weight: 900;
172
+ margin: 0;
173
+ padding: 12px 24px;
174
+ text-transform: uppercase;
175
+ letter-spacing: 1.5px;
176
+ `;
177
+
178
+ const WorkGrid = styled.div`
179
+ display: grid;
180
+ grid-template-columns: 140px 1fr;
181
+ border-bottom: 1px solid #666;
182
+
183
+ &:last-child {
184
+ border-bottom: none;
185
+ }
186
+ `;
187
+
188
+ const DateColumn = styled.div`
189
+ padding: 16px 12px;
190
+ border-right: 1px solid #666;
191
+ background: #fafafa;
192
+ font-size: 9px;
193
+ font-weight: 900;
194
+ text-transform: uppercase;
195
+ letter-spacing: 0.3px;
196
+ line-height: 1.6;
197
+ word-wrap: break-word;
198
+ `;
199
+
200
+ const ContentColumn = styled.div`
201
+ padding: 16px 24px;
202
+ `;
203
+
204
+ const WorkTitle = styled.h3`
205
+ font-size: 15px;
206
+ font-weight: 900;
207
+ margin: 0 0 6px 0;
208
+ text-transform: uppercase;
209
+ letter-spacing: -0.2px;
210
+ line-height: 1.2;
211
+ `;
212
+
213
+ const WorkCompany = styled.div`
214
+ font-size: 11px;
215
+ font-weight: 700;
216
+ margin-bottom: 10px;
217
+ text-transform: uppercase;
218
+ letter-spacing: 0.5px;
219
+ color: #333;
220
+ `;
221
+
222
+ const WorkDescription = styled.p`
223
+ font-size: 13px;
224
+ line-height: 1.5;
225
+ margin: 8px 0;
226
+ color: #333;
227
+ `;
228
+
229
+ const WorkHighlights = styled.ul`
230
+ margin: 8px 0 0 0;
231
+ padding-left: 20px;
232
+ list-style: square;
233
+
234
+ li {
235
+ margin-bottom: 4px;
236
+ font-size: 12px;
237
+ line-height: 1.4;
238
+ color: #333;
239
+ }
240
+ `;
241
+
242
+ const SimpleList = styled.div`
243
+ font-size: 11px;
244
+ line-height: 1.8;
245
+
246
+ div {
247
+ margin-bottom: 8px;
248
+
249
+ &:last-child {
250
+ margin-bottom: 0;
251
+ }
252
+ }
253
+ `;
254
+
255
+ function Resume({ resume }) {
256
+ const {
257
+ basics = {},
258
+ work = [],
259
+ education = [],
260
+ skills = [],
261
+ projects = [],
262
+ volunteer = [],
263
+ awards = [],
264
+ publications = [],
265
+ languages = [],
266
+ interests = [],
267
+ references = [],
268
+ } = resume;
269
+
270
+ return (
271
+ <Layout>
272
+ <Header>
273
+ <NameSection>
274
+ {basics.name && <Name>{basics.name}</Name>}
275
+ {basics.label && <Tagline>{basics.label}</Tagline>}
276
+ </NameSection>
277
+ <ContactSection>
278
+ {basics.email && <ContactItem>EMAIL: {basics.email}</ContactItem>}
279
+ {basics.phone && <ContactItem>PHONE: {basics.phone}</ContactItem>}
280
+ {basics.location?.city && basics.location?.region && (
281
+ <ContactItem>
282
+ LOCATION: {basics.location.city}, {basics.location.region}
283
+ </ContactItem>
284
+ )}
285
+ {basics.url && (
286
+ <ContactItem>
287
+ WEB: <a href={safeUrl(basics.url)}>{basics.url}</a>
288
+ </ContactItem>
289
+ )}
290
+ {basics.profiles?.slice(0, 2).map((profile, index) => (
291
+ <ContactItem key={index}>
292
+ {profile.network.toUpperCase()}:{' '}
293
+ <a href={safeUrl(profile.url)}>{profile.username}</a>
294
+ </ContactItem>
295
+ ))}
296
+ </ContactSection>
297
+ </Header>
298
+
299
+ <MainGrid>
300
+ <Sidebar>
301
+ {skills.length > 0 && (
302
+ <SidebarSection>
303
+ <SidebarSectionTitle>Skills</SidebarSectionTitle>
304
+ <SidebarContent>
305
+ {skills.map((skill, index) => (
306
+ <SkillItem key={index}>
307
+ <h4>{skill.name}</h4>
308
+ <StyledBadgeList>
309
+ {skill.keywords?.map((keyword, i) => (
310
+ <StyledBadge key={i}>{keyword}</StyledBadge>
311
+ ))}
312
+ </StyledBadgeList>
313
+ </SkillItem>
314
+ ))}
315
+ </SidebarContent>
316
+ </SidebarSection>
317
+ )}
318
+
319
+ {languages.length > 0 && (
320
+ <SidebarSection>
321
+ <SidebarSectionTitle>Languages</SidebarSectionTitle>
322
+ <SidebarContent>
323
+ <SimpleList>
324
+ {languages.map((lang, index) => (
325
+ <div key={index}>
326
+ <strong>{lang.language}</strong>
327
+ {lang.fluency && ` — ${lang.fluency}`}
328
+ </div>
329
+ ))}
330
+ </SimpleList>
331
+ </SidebarContent>
332
+ </SidebarSection>
333
+ )}
334
+
335
+ {interests.length > 0 && (
336
+ <SidebarSection>
337
+ <SidebarSectionTitle>Interests</SidebarSectionTitle>
338
+ <SidebarContent>
339
+ <SimpleList>
340
+ {interests.map((interest, index) => (
341
+ <div key={index}>{interest.name}</div>
342
+ ))}
343
+ </SimpleList>
344
+ </SidebarContent>
345
+ </SidebarSection>
346
+ )}
347
+ </Sidebar>
348
+
349
+ <MainContent>
350
+ {basics.summary && (
351
+ <MainSection>
352
+ <MainSectionTitle>Summary</MainSectionTitle>
353
+ <ContentColumn>
354
+ <WorkDescription>{basics.summary}</WorkDescription>
355
+ </ContentColumn>
356
+ </MainSection>
357
+ )}
358
+
359
+ {work.length > 0 && (
360
+ <MainSection>
361
+ <MainSectionTitle>Experience</MainSectionTitle>
362
+ {work.map((job, index) => (
363
+ <WorkGrid key={index}>
364
+ <DateColumn>
365
+ <DateRange
366
+ startDate={job.startDate}
367
+ endDate={job.endDate}
368
+ />
369
+ </DateColumn>
370
+ <ContentColumn>
371
+ <WorkTitle>{job.position || job.name}</WorkTitle>
372
+ {job.name && <WorkCompany>{job.name}</WorkCompany>}
373
+ {job.summary && (
374
+ <WorkDescription>{job.summary}</WorkDescription>
375
+ )}
376
+ {job.highlights && job.highlights.length > 0 && (
377
+ <WorkHighlights>
378
+ {job.highlights.map((highlight, i) => (
379
+ <li key={i}>{highlight}</li>
380
+ ))}
381
+ </WorkHighlights>
382
+ )}
383
+ </ContentColumn>
384
+ </WorkGrid>
385
+ ))}
386
+ </MainSection>
387
+ )}
388
+
389
+ {education.length > 0 && (
390
+ <MainSection>
391
+ <MainSectionTitle>Education</MainSectionTitle>
392
+ {education.map((edu, index) => (
393
+ <WorkGrid key={index}>
394
+ <DateColumn>
395
+ <DateRange
396
+ startDate={edu.startDate}
397
+ endDate={edu.endDate}
398
+ />
399
+ </DateColumn>
400
+ <ContentColumn>
401
+ <WorkTitle>{edu.institution}</WorkTitle>
402
+ {edu.studyType && edu.area && (
403
+ <WorkCompany>
404
+ {edu.studyType} in {edu.area}
405
+ </WorkCompany>
406
+ )}
407
+ {edu.score && (
408
+ <WorkDescription>Score: {edu.score}</WorkDescription>
409
+ )}
410
+ </ContentColumn>
411
+ </WorkGrid>
412
+ ))}
413
+ </MainSection>
414
+ )}
415
+
416
+ {projects.length > 0 && (
417
+ <MainSection>
418
+ <MainSectionTitle>Projects</MainSectionTitle>
419
+ {projects.map((project, index) => (
420
+ <WorkGrid key={index}>
421
+ <DateColumn>
422
+ <DateRange
423
+ startDate={project.startDate}
424
+ endDate={project.endDate}
425
+ />
426
+ </DateColumn>
427
+ <ContentColumn>
428
+ <WorkTitle>
429
+ {project.url ? (
430
+ <Link href={safeUrl(project.url)}>{project.name}</Link>
431
+ ) : (
432
+ project.name
433
+ )}
434
+ </WorkTitle>
435
+ {project.description && (
436
+ <WorkDescription>{project.description}</WorkDescription>
437
+ )}
438
+ {project.highlights && project.highlights.length > 0 && (
439
+ <WorkHighlights>
440
+ {project.highlights.map((highlight, i) => (
441
+ <li key={i}>{highlight}</li>
442
+ ))}
443
+ </WorkHighlights>
444
+ )}
445
+ </ContentColumn>
446
+ </WorkGrid>
447
+ ))}
448
+ </MainSection>
449
+ )}
450
+ </MainContent>
451
+ </MainGrid>
452
+ </Layout>
453
+ );
454
+ }
455
+
456
+ export default Resume;