jekyll-theme-resume 1.3.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 (123) hide show
  1. checksums.yaml +7 -0
  2. data/403.html +6 -0
  3. data/404.html +6 -0
  4. data/500.html +6 -0
  5. data/CHANGELOG.md +498 -0
  6. data/CODE_OF_CONDUCT.md +92 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +184 -0
  9. data/SECURITY.md +30 -0
  10. data/_config.sample.yml +329 -0
  11. data/_data/locales/ar.yml +85 -0
  12. data/_data/locales/de.yml +86 -0
  13. data/_data/locales/en.yml +84 -0
  14. data/_data/locales/es.yml +86 -0
  15. data/_data/locales/fr.yml +86 -0
  16. data/_data/locales/ur.yml +86 -0
  17. data/_data/social_networks.yml +83 -0
  18. data/_includes/analytics-body.html +12 -0
  19. data/_includes/analytics-head.html +36 -0
  20. data/_includes/avatar.html +84 -0
  21. data/_includes/dark-mode-toggle.html +202 -0
  22. data/_includes/data-loader.html +42 -0
  23. data/_includes/date-formatter.html +63 -0
  24. data/_includes/grouped-item-list.html +53 -0
  25. data/_includes/hreflang.html +58 -0
  26. data/_includes/language-switcher.html +72 -0
  27. data/_includes/print-social-links.html +23 -0
  28. data/_includes/resume-section.html +547 -0
  29. data/_includes/safe-url.html +21 -0
  30. data/_includes/shared-head.html +51 -0
  31. data/_includes/social-links.html +54 -0
  32. data/_includes/vendors/svg-icons/ATTRIBUTION.md +34 -0
  33. data/_includes/vendors/svg-icons/dev.svg +3 -0
  34. data/_includes/vendors/svg-icons/dribbble-symbol.svg +3 -0
  35. data/_includes/vendors/svg-icons/envelope.svg +4 -0
  36. data/_includes/vendors/svg-icons/facebook.svg +3 -0
  37. data/_includes/vendors/svg-icons/flickr.svg +4 -0
  38. data/_includes/vendors/svg-icons/github.svg +3 -0
  39. data/_includes/vendors/svg-icons/globe-1.svg +3 -0
  40. data/_includes/vendors/svg-icons/instagram.svg +4 -0
  41. data/_includes/vendors/svg-icons/linkedin.svg +3 -0
  42. data/_includes/vendors/svg-icons/medium.svg +3 -0
  43. data/_includes/vendors/svg-icons/phone.svg +13 -0
  44. data/_includes/vendors/svg-icons/pinterest.svg +3 -0
  45. data/_includes/vendors/svg-icons/postcard.svg +14 -0
  46. data/_includes/vendors/svg-icons/telegram.svg +3 -0
  47. data/_includes/vendors/svg-icons/whatsapp.svg +3 -0
  48. data/_includes/vendors/svg-icons/x.svg +3 -0
  49. data/_includes/vendors/svg-icons/youtube.svg +3 -0
  50. data/_layouts/default.html +65 -0
  51. data/_layouts/error.html +138 -0
  52. data/_layouts/profile.html +123 -0
  53. data/_layouts/resume.html +238 -0
  54. data/_plugins/error_pages_generator.rb +71 -0
  55. data/_plugins/json_resume_generator.rb +79 -0
  56. data/_plugins/resume_pages_generator.rb +71 -0
  57. data/_plugins/resume_validator.rb +40 -0
  58. data/_sass/_all-pages.scss +335 -0
  59. data/_sass/_base.scss +130 -0
  60. data/_sass/_dark-mode.scss +387 -0
  61. data/_sass/_layout.scss +116 -0
  62. data/_sass/_mixins.scss +136 -0
  63. data/_sass/_normalize.scss +379 -0
  64. data/_sass/_profile-page.scss +60 -0
  65. data/_sass/_resume-ltr.scss +436 -0
  66. data/_sass/_resume-rtl.scss +91 -0
  67. data/_sass/_variables.scss +45 -0
  68. data/assets/css/cv-ltr.scss +31 -0
  69. data/assets/css/cv-rtl.scss +36 -0
  70. data/assets/css/main.scss +8 -0
  71. data/assets/css/profile.scss +9 -0
  72. data/assets/favicon/resume/about.txt +6 -0
  73. data/assets/favicon/resume/android-chrome-192x192.png +0 -0
  74. data/assets/favicon/resume/android-chrome-512x512.png +0 -0
  75. data/assets/favicon/resume/apple-touch-icon.png +0 -0
  76. data/assets/favicon/resume/favicon-16x16.png +0 -0
  77. data/assets/favicon/resume/favicon-32x32.png +0 -0
  78. data/assets/favicon/resume/favicon.ico +0 -0
  79. data/assets/favicon/resume/site.webmanifest +1 -0
  80. data/bin/validate-resume +89 -0
  81. data/bin/verify +27 -0
  82. data/docs/README.md +89 -0
  83. data/docs/explanation/accessibility-decisions.md +27 -0
  84. data/docs/explanation/architecture.md +44 -0
  85. data/docs/explanation/dark-mode-approach.md +25 -0
  86. data/docs/explanation/data-driven-model.md +25 -0
  87. data/docs/explanation/multilingual-and-rtl-design.md +40 -0
  88. data/docs/how-to/add-a-language.md +57 -0
  89. data/docs/how-to/add-a-section.md +35 -0
  90. data/docs/how-to/add-a-social-platform.md +32 -0
  91. data/docs/how-to/add-a-test.md +34 -0
  92. data/docs/how-to/create-a-custom-layout.md +64 -0
  93. data/docs/how-to/enable-dark-mode.md +43 -0
  94. data/docs/how-to/link-translations-with-hreflang.md +25 -0
  95. data/docs/how-to/migrate-v0.9-to-v1.0.md +46 -0
  96. data/docs/how-to/override-locale-strings.md +51 -0
  97. data/docs/how-to/override-sass-partials.md +26 -0
  98. data/docs/how-to/proof-built-html.md +22 -0
  99. data/docs/how-to/publish-json-resume.md +38 -0
  100. data/docs/how-to/show-language-proficiency-in-header.md +13 -0
  101. data/docs/how-to/switch-resume-versions.md +42 -0
  102. data/docs/how-to/troubleshoot-builds.md +39 -0
  103. data/docs/how-to/validate-in-ci.md +38 -0
  104. data/docs/how-to/verify-accessibility.md +28 -0
  105. data/docs/reference/accessibility-coverage.md +39 -0
  106. data/docs/reference/config.md +378 -0
  107. data/docs/reference/data-schemas.md +529 -0
  108. data/docs/reference/glossary.md +39 -0
  109. data/docs/reference/includes.md +197 -0
  110. data/docs/reference/json-resume-fields.md +148 -0
  111. data/docs/reference/layouts.md +144 -0
  112. data/docs/reference/locale-keys.md +76 -0
  113. data/docs/reference/repository-map.md +83 -0
  114. data/docs/reference/sass-tokens.md +279 -0
  115. data/docs/reference/testing-suites.md +62 -0
  116. data/docs/reference/validator-cli.md +203 -0
  117. data/docs/tutorials/getting-started.md +173 -0
  118. data/lib/jekyll-theme-resume/json_resume_exporter.rb +334 -0
  119. data/lib/jekyll-theme-resume/resume_validator.rb +853 -0
  120. data/lib/jekyll-theme-resume/schemas/LICENSE.md +21 -0
  121. data/lib/jekyll-theme-resume/schemas/json_resume_v1.0.0.json +500 -0
  122. data/lib/jekyll-theme-resume.rb +21 -0
  123. metadata +371 -0
@@ -0,0 +1,547 @@
1
+ {% comment %}
2
+ Unified Section Dispatcher (all locales)
3
+ Renders a single resume section based on the 'section_name' parameter, replacing the
4
+ separate resume-section-en.html / resume-section-ar.html per-language dispatchers.
5
+ Usage: {% include resume-section.html section_name="experience" %}
6
+
7
+ Dispatcher Architecture:
8
+ - Iterated over by the active resume layout, looping through `site.resume_section_order`.
9
+ - Dual-Condition Gate: Each section is evaluated against `include.section_name == "<name>"`
10
+ and its configuration flag `site.resume_section.<name>` in `_config.yml`.
11
+ - Data Source: Binds to `resume_data.<section>` resolved dynamically by the parent layout.
12
+ - Locale Resolution: Resolves `lang`/`locale` the same way every other template does
13
+ (page.lang, falling back to site.default_lang), and reads section headers from
14
+ `locale.ui.section_titles`.
15
+ - Dates: Uses `_includes/date-formatter.html` for every date interval. A blank/omitted
16
+ `enddate` is passed through as "Present" (matched by every locale's `present_values`),
17
+ so ongoing roles render the locale's present-tense string automatically.
18
+ - RTL Bidi Protection: When `locale.direction == 'rtl'`, wraps URLs and credential IDs in
19
+ `dir="ltr"` so Latin punctuation and alphanumeric IDs aren't scrambled by the bidi
20
+ algorithm. No-op for LTR locales.
21
+ - Recognitions: Accepts only the plural `site.resume_section.recognitions` flag; the legacy
22
+ singular `site.resume_section.recognition` fallback is dropped (Task 3.4 confirms no
23
+ references remain).
24
+ - Microdata: Incorporates Schema.org semantic tags (Organization, CollegeOrUniversity, CreativeWork).
25
+ - Print Media: Hyperlinks include print-only text fallbacks to ensure URLs remain legible on physical paper or exported PDFs.
26
+ {% endcomment %}
27
+ {%- assign lang = include.lang | default: page.lang | default: site.default_lang | default: 'en' -%}
28
+ {%- assign locale = site.data.locales[lang] | default: site.data.locales[site.default_lang] -%}
29
+ {%- assign rtl = false -%}
30
+ {%- if locale.direction == 'rtl' -%}
31
+ {%- assign rtl = true -%}
32
+ {%- endif -%}
33
+
34
+ {%- comment -%}
35
+ Section: Experience
36
+ - Data: `resume_data.experience` filtered by `active: true`.
37
+ - Grouping: Groups multiple sequential roles under the same organization via `group_by: "company"`.
38
+ - Sorting: Sorts positions chronologically by `startdate` in reverse order (newest first).
39
+ - Formatting: Dynamically displays position, dates, and location with bullet separators (`&bull;`) only between non-empty fields.
40
+ - Durations vs Date Ranges: Renders explicit `durations` array if present (pre-formatted, not
41
+ passed through date-formatter.html), otherwise formats startdate to enddate/Present.
42
+ - Microdata: Schema.org `Organization` with `worksFor` itemprop.
43
+ {%- endcomment -%}
44
+ {% if include.section_name == "experience" and site.resume_section.experience %}
45
+ <!-- begin Experience -->
46
+ <section class="content-section">
47
+ <header class="section-header">
48
+ <h2>{{ locale.ui.section_titles.experience }}</h2>
49
+ </header>
50
+
51
+ {% include grouped-item-list.html items=resume_data.experience lang=lang %}
52
+ </section>
53
+ <!-- end Experience -->
54
+
55
+
56
+ {%- comment -%}
57
+ Section: Education
58
+ - Data: `resume_data.education` filtered by `active == true`.
59
+ - Microdata: Schema.org `CollegeOrUniversity` with `alumniOf` itemprop.
60
+ - Awards: Supports both single `award` string (h5) and multiple `awards` list (ul/li).
61
+ {%- endcomment -%}
62
+ {% elsif include.section_name == "education" and site.resume_section.education %}
63
+ <!-- begin Education -->
64
+ <section class="content-section">
65
+ <header class="section-header">
66
+ <h2>{{ locale.ui.section_titles.education }}</h2>
67
+ </header>
68
+
69
+ {% for education in resume_data.education %}
70
+ {% if education.active == true %}
71
+ <div class="resume-item" itemscope itemprop="alumniOf" itemtype="http://schema.org/CollegeOrUniversity">
72
+ <h3 class="resume-item-title" itemprop="name">{{ education.uni }}</h3>
73
+ {%- if education.degree.size > 0 or education.year.size > 0 or education.location.size > 0 -%}
74
+ <h4 class="resume-item-details group" itemprop="description">
75
+ {%- if education.degree.size > 0 -%}
76
+ {{ education.degree }}
77
+ {%- endif -%}
78
+ {%- if education.year.size > 0 -%}
79
+ {% if education.degree.size > 0 %} &bull; {% endif %}
80
+ {{ education.year }}
81
+ {%- endif -%}
82
+ {%- if education.location.size > 0 -%}
83
+ {% if education.degree.size > 0 or education.year.size > 0 %} &bull; {% endif %}
84
+ {{ education.location }}
85
+ {%- endif -%}
86
+ </h4>
87
+ {%- endif -%}
88
+
89
+ {% if education.award %}
90
+ <h5 class="resume-item-details award-title" itemprop="description">{{ education.award }}</h5>
91
+ {% endif %}
92
+
93
+ {% if education.awards and education.awards.size > 0 %}
94
+ <div class="resume-item-copy" itemprop="description">
95
+ <ul class="resume-item-list">
96
+ {% for award in education.awards %}
97
+ <li>{{ award.award }}</li>
98
+ {% endfor %}
99
+ </ul>
100
+ </div>
101
+ {% endif %}
102
+
103
+ {% if education.summary %}
104
+ <p class="resume-item-copy">{{ education.summary }}</p>
105
+ {% endif %}
106
+ </div>
107
+ {% endif %}
108
+ {% endfor %}
109
+ </section>
110
+ <!-- end Education -->
111
+
112
+ {%- comment -%}
113
+ Section: Licenses & Certifications
114
+ - Data: `resume_data.certifications` filtered by `active == true`.
115
+ - Date Localization: Formats issue and expiration dates with `date-formatter.html` (style="MDY").
116
+ - Credential Verification: Renders credential ID with optional verification link; wraps the
117
+ credential ID and the print-only URL fallback in dir="ltr" for RTL locales.
118
+ {%- endcomment -%}
119
+ {% elsif include.section_name == "certifications" and site.resume_section.certifications %}
120
+ <!-- begin Licenses & Certifications -->
121
+ <section class="content-section">
122
+ <header class="section-header">
123
+ <h2>{{ locale.ui.section_titles.certifications }}</h2>
124
+ </header>
125
+
126
+ {% for certification in resume_data.certifications %}
127
+ {% if certification.active == true %}
128
+ <div class="resume-item">
129
+ <h3 class="resume-item-title" itemprop="award">{{ certification.name }}</h3>
130
+ {%- if certification.issuing_organization.size > 0 or certification.issue_date or certification.credential_id.size > 0 -%}
131
+ <h4 class="resume-item-details">
132
+ {%- if certification.issuing_organization.size > 0 -%}
133
+ {{ certification.issuing_organization }}
134
+ {%- endif -%}
135
+ {%- if certification.issue_date -%}
136
+ {% if certification.issuing_organization.size > 0 %} &bull; {% endif %}
137
+ {% include date-formatter.html date=certification.issue_date lang=lang style="MDY" %}
138
+ {% if certification.expiration %} &ndash; {% include date-formatter.html date=certification.expiration lang=lang style="MDY" %} {% endif %}
139
+ {%- endif -%}
140
+ {%- if certification.credential_id.size > 0 -%}
141
+ {% if certification.issuing_organization.size > 0 or certification.issue_date %} &bull; {% endif %}
142
+ <span class="resume-item-copy-license-cert-id">{{ locale.ui.credential_id }}
143
+ {% if certification.credential_url and certification.credential_url != "" %}
144
+ <a href="{{ certification.credential_url }}" target="_blank" rel="noopener nofollow noreferrer"{% if rtl %} dir="ltr"{% endif %}>{{ certification.credential_id }}</a>
145
+ <span class="print-only-inline"{% if rtl %} dir="ltr"{% endif %}>&nbsp;({{ certification.credential_url }})</span>
146
+ {% else %}
147
+ <span{% if rtl %} dir="ltr"{% endif %}>{{ certification.credential_id }}</span>
148
+ {% endif %}
149
+ </span>
150
+ {%- endif -%}
151
+ </h4>
152
+ {%- endif -%}
153
+ </div>
154
+ {% endif %}
155
+ {% endfor %}
156
+ </section>
157
+ <!-- end Licenses & Certifications -->
158
+
159
+ {%- comment -%}
160
+ Section: Courses
161
+ - Data: `resume_data.courses` filtered by `active == true`.
162
+ - Content: Displays course name, issuing organization, date ranges (style="MDY"), summary,
163
+ and credential ID, with the same RTL dir="ltr" protection as Certifications.
164
+ {%- endcomment -%}
165
+ {% elsif include.section_name == "courses" and site.resume_section.courses %}
166
+ <!-- begin Courses -->
167
+ <section class="content-section">
168
+ <header class="section-header">
169
+ <h2>{{ locale.ui.section_titles.courses }}</h2>
170
+ </header>
171
+
172
+ {% for course in resume_data.courses %}
173
+ {% if course.active == true %}
174
+ <div class="resume-item">
175
+ <h3 class="resume-item-title" itemprop="award">{{ course.name }}</h3>
176
+ {%- if course.issuing_organization.size > 0 or course.startdate -%}
177
+ <h4 class="resume-item-details">
178
+ {%- if course.issuing_organization.size > 0 -%}
179
+ {{ course.issuing_organization }}
180
+ {%- endif -%}
181
+ {%- if course.startdate -%}
182
+ {% if course.issuing_organization.size > 0 %} &bull; {% endif %}
183
+ {% include date-formatter.html date=course.startdate lang=lang style="MDY" %}
184
+ {% if course.enddate %} &ndash; {% include date-formatter.html date=course.enddate lang=lang style="MDY" %} {% endif %}
185
+ {%- endif -%}
186
+ </h4>
187
+ {%- endif -%}
188
+
189
+ {% if site.enable_summary == true and course.summary and course.summary != "" %}
190
+ <p class="resume-item-copy">{{ course.summary }}</p>
191
+ {% endif %}
192
+
193
+ {% if course.credential_id and course.credential_id != "" %}
194
+ <p class="resume-item-copy">{{ locale.ui.credential_id }}
195
+ {% if course.credential_url and course.credential_url != "" %}
196
+ <a href="{{ course.credential_url }}" target="_blank" rel="noopener nofollow noreferrer"{% if rtl %} dir="ltr"{% endif %}>{{ course.credential_id }}</a>
197
+ <span class="print-only-inline"{% if rtl %} dir="ltr"{% endif %}>&nbsp;({{ course.credential_url }})</span>
198
+ {% else %}
199
+ <span{% if rtl %} dir="ltr"{% endif %}>{{ course.credential_id }}</span>
200
+ {% endif %}
201
+ </p>
202
+ {% endif %}
203
+ </div>
204
+ {% endif %}
205
+ {% endfor %}
206
+ </section>
207
+ <!-- end Courses -->
208
+
209
+ {%- comment -%}
210
+ Section: Volunteering
211
+ - Data: `resume_data.volunteering` filtered by `active: true`.
212
+ - Grouping: Groups volunteer positions under the same organization via `group_by: "company"`.
213
+ - Sorting: Reverse chronological order by `startdate`.
214
+ - Microdata: Schema.org `Organization` with `worksFor` itemprop.
215
+ {%- endcomment -%}
216
+ {% elsif include.section_name == "volunteering" and site.resume_section.volunteering %}
217
+ <!-- begin Volunteering -->
218
+ <section class="content-section">
219
+ <header class="section-header">
220
+ <h2>{{ locale.ui.section_titles.volunteering }}</h2>
221
+ </header>
222
+
223
+ {% include grouped-item-list.html items=resume_data.volunteering lang=lang %}
224
+ </section>
225
+ <!-- end Volunteering -->
226
+
227
+ {%- comment -%}
228
+ Section: Projects
229
+ - Data: `resume_data.projects` filtered by `active == true`.
230
+ - Microdata: Schema.org `CreativeWork` with author/creator person metadata.
231
+ - Links: External project URLs with `target="_blank" rel="noopener nofollow noreferrer"` and
232
+ inline print URL fallbacks, wrapped in dir="ltr" for RTL locales.
233
+ {%- endcomment -%}
234
+ {% elsif include.section_name == "projects" and site.resume_section.projects %}
235
+ <!-- begin Projects -->
236
+ <section class="content-section">
237
+ <header class="section-header">
238
+ <h2>{{ locale.ui.section_titles.projects }}</h2>
239
+ </header>
240
+
241
+ {% for project in resume_data.projects %}
242
+ {% if project.active == true %}
243
+ <div class="resume-item" itemscope itemtype="http://schema.org/CreativeWork">
244
+ <meta itemprop="creator" content="{{ lang_cfg.name }}" itemtype="http://schema.org/Person" />
245
+ <h3 class="resume-item-title" itemprop="name" style="display: inline-block;">
246
+ {% if project.url and project.url != "" %}
247
+ <a href="{{ project.url }}" target="_blank" rel="noopener nofollow noreferrer" itemprop="url">{{ project.project }}</a>
248
+ {% else %}
249
+ {{ project.project }}
250
+ {% endif %}
251
+ </h3>
252
+
253
+ {% if project.url and project.url != "" %}
254
+ <span class="print-only-inline"{% if rtl %} dir="ltr"{% endif %} style="display: inline;">&nbsp;{{ project.url }}</span>
255
+ {% endif %}
256
+
257
+ {%- if project.role.size > 0 or project.duration.size > 0 -%}
258
+ <h4 class="resume-item-details" itemprop="description">
259
+ {%- if project.role.size > 0 -%}
260
+ {{ project.role }}
261
+ {%- endif -%}
262
+ {%- if project.duration.size > 0 -%}
263
+ {% if project.role.size > 0 %} &bull; {% endif %}
264
+ {{ project.duration }}
265
+ {%- endif -%}
266
+ </h4>
267
+ {%- endif -%}
268
+ <p class="resume-item-copy">{{ project.description }}</p>
269
+ </div>
270
+ {% endif %}
271
+ {% endfor %}
272
+ </section>
273
+ <!-- end Projects -->
274
+
275
+ {%- comment -%}
276
+ Section: Skills
277
+ - Data: `resume_data.skills` filtered by `active == true`.
278
+ - Formatting: Displays categorized competencies and skill descriptions.
279
+ {%- endcomment -%}
280
+ {% elsif include.section_name == "skills" and site.resume_section.skills %}
281
+ <!-- begin Skills -->
282
+ <section class="content-section">
283
+ <header class="section-header">
284
+ <h2>{{ locale.ui.section_titles.skills }}</h2>
285
+ </header>
286
+
287
+ {% for skill in resume_data.skills %}
288
+ {% if skill.active == true %}
289
+ <div class="resume-item">
290
+ <h4 class="resume-item-details">{{ skill.skill }}</h4>
291
+ {% if skill.description %}
292
+ <p class="resume-item-copy">{{ skill.description }}</p>
293
+ {% endif %}
294
+ </div>
295
+ {% endif %}
296
+ {% endfor %}
297
+ </section>
298
+ <!-- end Skills -->
299
+
300
+ {%- comment -%}
301
+ Section: Recognitions / Awards
302
+ - Data: `resume_data.recognitions` filtered by `active == true`.
303
+ - Content: Displays award name, issuing organization, year, and summary.
304
+ - Only the plural `site.resume_section.recognitions` flag is honored.
305
+ {%- endcomment -%}
306
+ {% elsif include.section_name == "recognitions" and site.resume_section.recognitions %}
307
+ <!-- begin Recognition -->
308
+ <section class="content-section">
309
+ <header class="section-header">
310
+ <h2>{{ locale.ui.section_titles.recognitions }}</h2>
311
+ </header>
312
+
313
+ {% for recognition in resume_data.recognitions %}
314
+ {% if recognition.active == true %}
315
+ <div class="resume-item">
316
+ <h3 class="resume-item-title" itemprop="award">{{ recognition.award }}</h3>
317
+ {%- if recognition.organization.size > 0 or recognition.year.size > 0 -%}
318
+ <h4 class="resume-item-details">
319
+ {%- if recognition.organization.size > 0 -%}
320
+ {{ recognition.organization }}
321
+ {%- endif -%}
322
+ {%- if recognition.year.size > 0 -%}
323
+ {% if recognition.organization.size > 0 %} &bull; {% endif %}
324
+ {{ recognition.year }}
325
+ {%- endif -%}
326
+ </h4>
327
+ {%- endif -%}
328
+ {% if recognition.summary %}
329
+ <p class="resume-item-copy">{{ recognition.summary }}</p>
330
+ {% endif %}
331
+ </div>
332
+ {% endif %}
333
+ {% endfor %}
334
+ </section>
335
+ <!-- end Recognition -->
336
+
337
+ {%- comment -%}
338
+ Section: Associations & Professional Memberships
339
+ - Data: `resume_data.associations` filtered by `active == true`.
340
+ - Microdata: Schema.org `Organization` with `memberOf` itemprop.
341
+ - Links: External organization URLs with print fallback, wrapped in dir="ltr" for RTL locales.
342
+ {%- endcomment -%}
343
+ {% elsif include.section_name == "associations" and site.resume_section.associations %}
344
+ <!-- begin Associations -->
345
+ <section class="content-section">
346
+ <header class="section-header">
347
+ <h2>{{ locale.ui.section_titles.associations }}</h2>
348
+ </header>
349
+
350
+ {% for association in resume_data.associations %}
351
+ {% if association.active == true %}
352
+ <div class="resume-item" itemscope itemprop="memberOf" itemtype="http://schema.org/Organization">
353
+ <h3 class="resume-item-title" itemprop="name" style="display: inline-block;">
354
+ {% if association.url and association.url != "" %}
355
+ <a href="{{ association.url }}" target="_blank" rel="noopener nofollow noreferrer">{{ association.organization }}</a>
356
+ {% else %}
357
+ {{ association.organization }}
358
+ {% endif %}
359
+ </h3>
360
+
361
+ {% if association.url and association.url != "" %}
362
+ <span class="print-only-inline"{% if rtl %} dir="ltr"{% endif %} style="display: inline;">&nbsp;{{ association.url }}</span>
363
+ {% endif %}
364
+
365
+ {%- if association.role.size > 0 or association.year.size > 0 -%}
366
+ <h4 class="resume-item-details" itemprop="description">
367
+ {%- if association.role.size > 0 -%}
368
+ {{ association.role }}
369
+ {%- endif -%}
370
+ {%- if association.year.size > 0 -%}
371
+ {% if association.role.size > 0 %} &bull; {% endif %}
372
+ {{ association.year }}
373
+ {%- endif -%}
374
+ </h4>
375
+ {%- endif -%}
376
+ {% if association.summary %}
377
+ <p class="resume-item-copy">{{ association.summary }}</p>
378
+ {% endif %}
379
+ </div>
380
+ {% endif %}
381
+ {% endfor %}
382
+ </section>
383
+ <!-- end Associations -->
384
+
385
+ {%- comment -%}
386
+ Section: Outside Interests
387
+ - Data: `resume_data.interests`.
388
+ - Formatting: Unordered list highlighting personal hobbies and extracurricular activities.
389
+ {%- endcomment -%}
390
+ {% elsif include.section_name == "interests" and site.resume_section.interests %}
391
+ <!-- begin Interests -->
392
+ <section class="content-section">
393
+ <header class="section-header">
394
+ <h2>{{ locale.ui.section_titles.interests }}</h2>
395
+ </header>
396
+
397
+ <div class="resume-item">
398
+ <ul class="resume-item-list">
399
+ {% for interest in resume_data.interests %}
400
+ <li>{{ interest.description }}</li>
401
+ {% endfor %}
402
+ </ul>
403
+ </div>
404
+ </section>
405
+ <!-- end Interests -->
406
+
407
+ {%- comment -%}
408
+ Section: Languages
409
+ - Display Placement Check: Rendered as a distinct section only if `site.resume_section.lang_header != true`
410
+ (when lang_header is enabled, languages appear in the resume header instead).
411
+ - 2-Column Balanced Table: Uses Liquid `modulo: 2` arithmetic to balance entries into rows.
412
+ Injects an empty `<td class="lang-cell"></td>` on the final odd entry to maintain valid HTML table structure.
413
+ {%- endcomment -%}
414
+ {% elsif include.section_name == "languages" and site.resume_section.languages and site.resume_section.lang_header != true %}
415
+ <!-- begin Languages -->
416
+ <section class="content-section">
417
+ <header class="section-header">
418
+ <h2>{{ locale.ui.section_titles.languages }}</h2>
419
+ </header>
420
+
421
+ {%- assign active_languages = resume_data.languages | where: "active", true -%}
422
+ <table class="languages-table">
423
+ <tbody>
424
+ {%- for language in active_languages -%}
425
+ {%- assign mod = forloop.index0 | modulo: 2 -%}
426
+ {%- if mod == 0 -%}
427
+ <tr>
428
+ {%- endif -%}
429
+
430
+ <td class="lang-cell">
431
+ <p class="lang-entry">
432
+ <span class="resume-item-details">{{ language.language }}</span>
433
+ &ndash;
434
+ <span class="resume-item-copy">{{ language.description }}</span>
435
+ </p>
436
+ </td>
437
+
438
+ {%- if mod == 1 or forloop.last -%}
439
+ {%- if forloop.last and mod == 0 -%}
440
+ <td class="lang-cell"></td>
441
+ {%- endif -%}
442
+ </tr>
443
+ {%- endif -%}
444
+ {%- endfor -%}
445
+ </tbody>
446
+ </table>
447
+ </section>
448
+ <!-- end Languages -->
449
+
450
+ {%- comment -%}
451
+ Section: Additional Links
452
+ - Data: `resume_data.links` filtered by `active == true`.
453
+ - Print Media: Accompanied by inline plaintext URLs for print readability, wrapped in
454
+ dir="ltr" for RTL locales.
455
+ {%- endcomment -%}
456
+ {% elsif include.section_name == "links" and site.resume_section.links %}
457
+ <!-- begin Links -->
458
+ <section class="content-section">
459
+ <header class="section-header">
460
+ <h2>{{ locale.ui.section_titles.links }}</h2>
461
+ </header>
462
+
463
+ <div class="resume-item">
464
+ <ul class="resume-item-list">
465
+ {% for link in resume_data.links %}
466
+ {% if link.active == true %}
467
+ <li>
468
+ <a href="{{ link.url }}" target="_blank" rel="noopener nofollow noreferrer" itemprop="url">{{ link.description }}</a>
469
+ <span class="print-only-inline"{% if rtl %} dir="ltr"{% endif %}>&nbsp;{{ link.url }}</span>
470
+ </li>
471
+ {% endif %}
472
+ {% endfor %}
473
+ </ul>
474
+ </div>
475
+ </section>
476
+ <!-- end Links -->
477
+
478
+ {%- comment -%}
479
+ Section: Publications
480
+ - Data: `resume_data.publications` filtered by `active == true`.
481
+ - Content: Name (linked to `url` when present, with a print-only URL fallback in dir="ltr" for
482
+ RTL locales), publisher and release date (style="MDY"), then the summary (always shown).
483
+ {%- endcomment -%}
484
+ {% elsif include.section_name == "publications" and site.resume_section.publications %}
485
+ <!-- begin Publications -->
486
+ <section class="content-section">
487
+ <header class="section-header">
488
+ <h2>{{ locale.ui.section_titles.publications }}</h2>
489
+ </header>
490
+
491
+ {% for publication in resume_data.publications %}
492
+ {% if publication.active == true %}
493
+ <div class="resume-item" itemscope itemtype="https://schema.org/CreativeWork">
494
+ <h3 class="resume-item-title" itemprop="name">
495
+ {%- if publication.url and publication.url != "" -%}
496
+ <a href="{{ publication.url }}" target="_blank" rel="noopener nofollow noreferrer" itemprop="url">{{ publication.name }}</a>
497
+ <span class="print-only-inline"{% if rtl %} dir="ltr"{% endif %}>&nbsp;{{ publication.url }}</span>
498
+ {%- else -%}
499
+ {{ publication.name }}
500
+ {%- endif -%}
501
+ </h3>
502
+ {%- if publication.publisher.size > 0 or publication.release_date -%}
503
+ <h4 class="resume-item-details">
504
+ {%- if publication.publisher.size > 0 -%}
505
+ <span itemprop="publisher">{{ publication.publisher }}</span>
506
+ {%- endif -%}
507
+ {%- if publication.release_date -%}
508
+ {% if publication.publisher.size > 0 %} &bull; {% endif %}
509
+ {% include date-formatter.html date=publication.release_date lang=lang style="MDY" %}
510
+ {%- endif -%}
511
+ </h4>
512
+ {%- endif -%}
513
+ {% if publication.summary %}
514
+ <p class="resume-item-copy" itemprop="description">{{ publication.summary }}</p>
515
+ {% endif %}
516
+ </div>
517
+ {% endif %}
518
+ {% endfor %}
519
+ </section>
520
+ <!-- end Publications -->
521
+
522
+ {%- comment -%}
523
+ Section: References
524
+ - Data: `resume_data.references` filtered by `active == true`.
525
+ - Content: A `<blockquote>` with the reference text and a `<cite>` naming the referee.
526
+ {%- endcomment -%}
527
+ {% elsif include.section_name == "references" and site.resume_section.references %}
528
+ <!-- begin References -->
529
+ <section class="content-section">
530
+ <header class="section-header">
531
+ <h2>{{ locale.ui.section_titles.references }}</h2>
532
+ </header>
533
+
534
+ {% for reference in resume_data.references %}
535
+ {% if reference.active == true %}
536
+ <div class="resume-item">
537
+ <blockquote>
538
+ <p>{{ reference.reference }}</p>
539
+ <cite>{{ reference.name }}</cite>
540
+ </blockquote>
541
+ </div>
542
+ {% endif %}
543
+ {% endfor %}
544
+ </section>
545
+ <!-- end References -->
546
+
547
+ {% endif %}
@@ -0,0 +1,21 @@
1
+ {%- comment -%}
2
+ Include: URL scheme allowlist
3
+ - Purpose: Stop `javascript:`, `data:` and similar schemes in config-supplied URLs reaching href/src.
4
+ - Parameters:
5
+ - url: The value to check.
6
+ - schemes: Comma-separated allowed schemes, e.g. "http,https".
7
+ - Output: sets `safe_url` to the trimmed value, or to nil when it is blank or its scheme is not allowed.
8
+ - Rule: a value containing ":" must start with an allowed scheme (case-insensitive); a value without ":"
9
+ is a relative path and is allowed. Keep in sync with ResumeValidator.safe_url? (same rule, Ruby side).
10
+ {%- endcomment -%}
11
+ {%- assign safe_url = nil -%}
12
+ {%- assign safe_url_raw = include.url | append: "" | strip -%}
13
+ {%- if safe_url_raw contains ":" -%}
14
+ {%- assign safe_url_scheme = safe_url_raw | split: ":" | first | downcase -%}
15
+ {%- assign safe_url_allowed = include.schemes | split: "," -%}
16
+ {%- if safe_url_allowed contains safe_url_scheme -%}
17
+ {%- assign safe_url = safe_url_raw -%}
18
+ {%- endif -%}
19
+ {%- elsif safe_url_raw != "" -%}
20
+ {%- assign safe_url = safe_url_raw -%}
21
+ {%- endif -%}
@@ -0,0 +1,51 @@
1
+ {%- comment -%}
2
+ Shared Head Include
3
+ - Injected by all layouts (`resume.html`, `default.html`, `profile.html`, `error.html`).
4
+ - Responsiveness & Charset: Configures UTF-8, edge compatibility, and responsive viewport with viewport-fit=cover for mobile screens.
5
+ - Anti-FOUC Theme Initialization: Synchronously evaluates `localStorage` before page paint to set `data-color-scheme` and `data-theme` on the root <html> element, preventing light-mode flash for dark-mode users.
6
+ - Favicon Suite: Resolves multi-resolution favicons and web manifest with fallback to defaults and `relative_url` filtering.
7
+ - Robots Indexing: Conditionally emits noindex directives if `page.noindex == true` (e.g. for error pages).
8
+ {%- endcomment -%}
9
+ <meta charset="UTF-8">
10
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
11
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
12
+ <meta name="color-scheme" content="light dark">
13
+ {% if page.layout == 'resume' %}
14
+ {% assign json_resume_lang = page.lang | default: site.default_lang | default: 'en' %}
15
+ {% assign json_resume_route = site.json_resume_routes[json_resume_lang] %}
16
+ {% if json_resume_route %}
17
+ <link rel="alternate" type="application/json" href="{{ json_resume_route | relative_url | escape }}" hreflang="{{ json_resume_lang | escape }}">
18
+ {% endif %}
19
+ {% endif %}
20
+ <script>
21
+ (function() {
22
+ try {
23
+ var saved = localStorage.getItem('color-scheme');
24
+ if (saved === 'dark' || saved === 'light') {
25
+ document.documentElement.setAttribute('data-color-scheme', saved);
26
+ document.documentElement.setAttribute('data-theme', saved);
27
+ var meta = document.querySelector('meta[name="color-scheme"]');
28
+ if (meta) {
29
+ meta.setAttribute('content', saved);
30
+ }
31
+ }
32
+ } catch (e) {
33
+ // Graceful fallback if localStorage is restricted (private browsing / iframe sandboxing)
34
+ }
35
+ })();
36
+ </script>
37
+
38
+ <!-- Favicons -->
39
+ {% assign favicon_path = site.favicon | default: 'assets/favicon/resume/favicon.ico' %}
40
+ <link rel="shortcut icon" href="{{ favicon_path | relative_url }}" type="image/x-icon">
41
+ <link rel="icon" href="{{ favicon_path | relative_url }}" type="image/x-icon">
42
+ <link rel="apple-touch-icon" sizes="180x180" href="{{ site.apple_touch_icon | default: 'assets/favicon/resume/apple-touch-icon.png' | relative_url }}">
43
+ <link rel="icon" type="image/png" sizes="32x32" href="{{ site.favicon_32 | default: 'assets/favicon/resume/favicon-32x32.png' | relative_url }}">
44
+ <link rel="icon" type="image/png" sizes="16x16" href="{{ site.favicon_16 | default: 'assets/favicon/resume/favicon-16x16.png' | relative_url }}">
45
+ <link rel="manifest" href="{{ 'assets/favicon/resume/site.webmanifest' | relative_url }}">
46
+
47
+ {% if page.noindex == true -%}
48
+ <meta name="robots" content="noindex noarchive nosnippet noimageindex">
49
+ {%- else -%}
50
+ <meta name="robots" content="all">
51
+ {%- endif -%}