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.
- checksums.yaml +7 -0
- data/403.html +6 -0
- data/404.html +6 -0
- data/500.html +6 -0
- data/CHANGELOG.md +498 -0
- data/CODE_OF_CONDUCT.md +92 -0
- data/LICENSE.txt +22 -0
- data/README.md +184 -0
- data/SECURITY.md +30 -0
- data/_config.sample.yml +329 -0
- data/_data/locales/ar.yml +85 -0
- data/_data/locales/de.yml +86 -0
- data/_data/locales/en.yml +84 -0
- data/_data/locales/es.yml +86 -0
- data/_data/locales/fr.yml +86 -0
- data/_data/locales/ur.yml +86 -0
- data/_data/social_networks.yml +83 -0
- data/_includes/analytics-body.html +12 -0
- data/_includes/analytics-head.html +36 -0
- data/_includes/avatar.html +84 -0
- data/_includes/dark-mode-toggle.html +202 -0
- data/_includes/data-loader.html +42 -0
- data/_includes/date-formatter.html +63 -0
- data/_includes/grouped-item-list.html +53 -0
- data/_includes/hreflang.html +58 -0
- data/_includes/language-switcher.html +72 -0
- data/_includes/print-social-links.html +23 -0
- data/_includes/resume-section.html +547 -0
- data/_includes/safe-url.html +21 -0
- data/_includes/shared-head.html +51 -0
- data/_includes/social-links.html +54 -0
- data/_includes/vendors/svg-icons/ATTRIBUTION.md +34 -0
- data/_includes/vendors/svg-icons/dev.svg +3 -0
- data/_includes/vendors/svg-icons/dribbble-symbol.svg +3 -0
- data/_includes/vendors/svg-icons/envelope.svg +4 -0
- data/_includes/vendors/svg-icons/facebook.svg +3 -0
- data/_includes/vendors/svg-icons/flickr.svg +4 -0
- data/_includes/vendors/svg-icons/github.svg +3 -0
- data/_includes/vendors/svg-icons/globe-1.svg +3 -0
- data/_includes/vendors/svg-icons/instagram.svg +4 -0
- data/_includes/vendors/svg-icons/linkedin.svg +3 -0
- data/_includes/vendors/svg-icons/medium.svg +3 -0
- data/_includes/vendors/svg-icons/phone.svg +13 -0
- data/_includes/vendors/svg-icons/pinterest.svg +3 -0
- data/_includes/vendors/svg-icons/postcard.svg +14 -0
- data/_includes/vendors/svg-icons/telegram.svg +3 -0
- data/_includes/vendors/svg-icons/whatsapp.svg +3 -0
- data/_includes/vendors/svg-icons/x.svg +3 -0
- data/_includes/vendors/svg-icons/youtube.svg +3 -0
- data/_layouts/default.html +65 -0
- data/_layouts/error.html +138 -0
- data/_layouts/profile.html +123 -0
- data/_layouts/resume.html +238 -0
- data/_plugins/error_pages_generator.rb +71 -0
- data/_plugins/json_resume_generator.rb +79 -0
- data/_plugins/resume_pages_generator.rb +71 -0
- data/_plugins/resume_validator.rb +40 -0
- data/_sass/_all-pages.scss +335 -0
- data/_sass/_base.scss +130 -0
- data/_sass/_dark-mode.scss +387 -0
- data/_sass/_layout.scss +116 -0
- data/_sass/_mixins.scss +136 -0
- data/_sass/_normalize.scss +379 -0
- data/_sass/_profile-page.scss +60 -0
- data/_sass/_resume-ltr.scss +436 -0
- data/_sass/_resume-rtl.scss +91 -0
- data/_sass/_variables.scss +45 -0
- data/assets/css/cv-ltr.scss +31 -0
- data/assets/css/cv-rtl.scss +36 -0
- data/assets/css/main.scss +8 -0
- data/assets/css/profile.scss +9 -0
- data/assets/favicon/resume/about.txt +6 -0
- data/assets/favicon/resume/android-chrome-192x192.png +0 -0
- data/assets/favicon/resume/android-chrome-512x512.png +0 -0
- data/assets/favicon/resume/apple-touch-icon.png +0 -0
- data/assets/favicon/resume/favicon-16x16.png +0 -0
- data/assets/favicon/resume/favicon-32x32.png +0 -0
- data/assets/favicon/resume/favicon.ico +0 -0
- data/assets/favicon/resume/site.webmanifest +1 -0
- data/bin/validate-resume +89 -0
- data/bin/verify +27 -0
- data/docs/README.md +89 -0
- data/docs/explanation/accessibility-decisions.md +27 -0
- data/docs/explanation/architecture.md +44 -0
- data/docs/explanation/dark-mode-approach.md +25 -0
- data/docs/explanation/data-driven-model.md +25 -0
- data/docs/explanation/multilingual-and-rtl-design.md +40 -0
- data/docs/how-to/add-a-language.md +57 -0
- data/docs/how-to/add-a-section.md +35 -0
- data/docs/how-to/add-a-social-platform.md +32 -0
- data/docs/how-to/add-a-test.md +34 -0
- data/docs/how-to/create-a-custom-layout.md +64 -0
- data/docs/how-to/enable-dark-mode.md +43 -0
- data/docs/how-to/link-translations-with-hreflang.md +25 -0
- data/docs/how-to/migrate-v0.9-to-v1.0.md +46 -0
- data/docs/how-to/override-locale-strings.md +51 -0
- data/docs/how-to/override-sass-partials.md +26 -0
- data/docs/how-to/proof-built-html.md +22 -0
- data/docs/how-to/publish-json-resume.md +38 -0
- data/docs/how-to/show-language-proficiency-in-header.md +13 -0
- data/docs/how-to/switch-resume-versions.md +42 -0
- data/docs/how-to/troubleshoot-builds.md +39 -0
- data/docs/how-to/validate-in-ci.md +38 -0
- data/docs/how-to/verify-accessibility.md +28 -0
- data/docs/reference/accessibility-coverage.md +39 -0
- data/docs/reference/config.md +378 -0
- data/docs/reference/data-schemas.md +529 -0
- data/docs/reference/glossary.md +39 -0
- data/docs/reference/includes.md +197 -0
- data/docs/reference/json-resume-fields.md +148 -0
- data/docs/reference/layouts.md +144 -0
- data/docs/reference/locale-keys.md +76 -0
- data/docs/reference/repository-map.md +83 -0
- data/docs/reference/sass-tokens.md +279 -0
- data/docs/reference/testing-suites.md +62 -0
- data/docs/reference/validator-cli.md +203 -0
- data/docs/tutorials/getting-started.md +173 -0
- data/lib/jekyll-theme-resume/json_resume_exporter.rb +334 -0
- data/lib/jekyll-theme-resume/resume_validator.rb +853 -0
- data/lib/jekyll-theme-resume/schemas/LICENSE.md +21 -0
- data/lib/jekyll-theme-resume/schemas/json_resume_v1.0.0.json +500 -0
- data/lib/jekyll-theme-resume.rb +21 -0
- metadata +371 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
# Configuration reference (`_config.yml`)
|
|
2
|
+
|
|
3
|
+
*Audience: site owners*
|
|
4
|
+
|
|
5
|
+
Every setting the theme reads from a consuming site's `_config.yml`. The annotated master copy is [`_config.sample.yml`](../../_config.sample.yml); the build reads your site’s `_config.yml`, not the sample automatically. Templates, generators, and the gemspec define current behavior.
|
|
6
|
+
|
|
7
|
+
Per-language settings (name, title, address, data path, URL) live under `languages.<lang>`. Direction, fonts, and UI strings are not config at all: they come from `_data/locales/<lang>.yml` (see [Locale keys](locale-keys.md)). Upgrading from v0.9.0? See [Migrate from v0.9 to v1.0](../how-to/migrate-v0.9-to-v1.0.md). The requirements (Ruby, Jekyll, loading the theme through `group :jekyll_plugins`) are listed in [Getting started](../tutorials/getting-started.md).
|
|
8
|
+
|
|
9
|
+
## Configuration Reference
|
|
10
|
+
|
|
11
|
+
### 1. Site Identity
|
|
12
|
+
|
|
13
|
+
| Setting | Type | Default | Description |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| `theme` | String | | **Required.** `jekyll-theme-resume`. |
|
|
16
|
+
| `title` | String | `""` | **Required.** Site title for SEO tags, avatar link title, and footers. |
|
|
17
|
+
| `description` | String | `""` | Site summary emitted by `{% seo %}`. |
|
|
18
|
+
| `url` | String | `""` | **Required.** Protocol and domain (e.g., `https://example.com`). Used for absolute hreflang URLs. |
|
|
19
|
+
| `baseurl` | String | `""` | Subdirectory path if the site is not served from the domain root. |
|
|
20
|
+
| `timezone` | String | unset (system time zone; the sample sets `UTC`) | Timezone for date rendering (e.g., `America/New_York`, `Asia/Riyadh`). Jekyll core key. |
|
|
21
|
+
|
|
22
|
+
The page language and text direction are not site settings. Every layout resolves the language from `page.lang`, then `default_lang`, then `en`, and reads `direction` from that language's locale file.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
### 2. Favicons & Web App Manifest
|
|
27
|
+
|
|
28
|
+
The theme ships a favicon suite under `assets/favicon/resume/`. Override any asset with a site-level path:
|
|
29
|
+
|
|
30
|
+
| Setting | Type | Default | Description |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| `favicon` | String | `"assets/favicon/resume/favicon.ico"` | Primary `.ico` shortcut icon. |
|
|
33
|
+
| `apple_touch_icon` | String | `"assets/favicon/resume/apple-touch-icon.png"` | 180x180 PNG icon for iOS home screens. |
|
|
34
|
+
| `favicon_32` | String | `"assets/favicon/resume/favicon-32x32.png"` | 32x32 browser favicon. |
|
|
35
|
+
| `favicon_16` | String | `"assets/favicon/resume/favicon-16x16.png"` | 16x16 browser favicon. |
|
|
36
|
+
|
|
37
|
+
All favicon paths pass through `relative_url` in [`_includes/shared-head.html`](../../_includes/shared-head.html), so they work under a `baseurl`.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
### 3. Languages
|
|
42
|
+
|
|
43
|
+
One entry per language, keyed by language code. The same code names the page's `lang`, the locale file `_data/locales/<code>.yml`, and (usually) the data folder.
|
|
44
|
+
|
|
45
|
+
| Setting | Type | Description |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `languages.<lang>.data_path` | String | **Required.** Data folder under `_data/`. Dot paths select nested folders (`"2025-06.v1"` reads `_data/2025-06/v1/`); `""` reads `_data/` itself. |
|
|
48
|
+
| `languages.<lang>.url` | String | CV permalink used by page generation, profile CV buttons, the language switcher fallback, and error Home buttons when no profile page exists. |
|
|
49
|
+
| `languages.<lang>.about` | String | Profile-page bio; accepts Markdown, HTML, or plain text. |
|
|
50
|
+
| `languages.<lang>.name_html` | String | Optional profile-page name markup. Without it the final space-separated word in `name` is bolded. Keep `name` plain text for other uses. |
|
|
51
|
+
| `languages.<lang>.header_intro` | Boolean | `true` renders `intro` from this language's `header.yml` below the header. |
|
|
52
|
+
| `languages.<lang>.name` | String | Full name shown in the resume header. |
|
|
53
|
+
| `languages.<lang>.resume_title` | String | Job title shown under the name. |
|
|
54
|
+
| `languages.<lang>.address` | String | Location shown in the contact row and in Schema.org microdata. |
|
|
55
|
+
| `languages.<lang>.avatar_alt` | String | Avatar alt text. Falls back to `name`, then the locale's `ui.photo_alt`. |
|
|
56
|
+
| `languages.<lang>.postal_code`, `city`, `country_code`, `region` | String | Optional. Exported as `basics.location.postalCode`, `city`, `countryCode`, and `region` in the JSON Resume file; not shown on the page. An invalid `country_code` is omitted with a warning. |
|
|
57
|
+
| `languages.<lang>.auto_generate_pages` | Boolean | Per-language override of `resume_auto_generate_pages` below. Set `false` to require a hand-authored page for just this language even when auto-generation is on globally, or `true` to auto-generate just this language even when it's off globally. |
|
|
58
|
+
| `default_lang` | String | Language used when a page has no `lang`, for the hreflang `x-default` link, and for error page button labels. Default `en`. |
|
|
59
|
+
| `resume_auto_generate_pages` | Boolean | Default `true`. When a `languages.<lang>` entry has no hand-authored CV (`layout: resume`) or profile (`layout: profile`) page, the theme synthesizes one automatically at `languages.<lang>.url` (CV) and `/` for `default_lang` or `/<lang>/` for any other language (profile), each carrying `t_id: resume` / `t_id: profile` so hreflang and the language switcher match them like any hand-authored page. A hand-authored page for a given `layout`+`lang` always wins over auto-generation. Set `false` to require every language to have its own hand-authored page; a language left without a page (auto-generation off and no hand-authored file) logs a build warning instead of failing silently. |
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
languages:
|
|
63
|
+
en:
|
|
64
|
+
data_path: en
|
|
65
|
+
url: /en/cv/
|
|
66
|
+
header_intro: true
|
|
67
|
+
name: "Jane Doe"
|
|
68
|
+
resume_title: "Senior Product Manager"
|
|
69
|
+
address: "San Francisco, CA"
|
|
70
|
+
avatar_alt: "Jane Doe - Professional Profile"
|
|
71
|
+
ar:
|
|
72
|
+
data_path: ar
|
|
73
|
+
url: /ar/cv/
|
|
74
|
+
header_intro: true
|
|
75
|
+
name: "جين دو"
|
|
76
|
+
resume_title: "مديرة منتج أولى"
|
|
77
|
+
address: "سان فرانسيسكو، كاليفورنيا"
|
|
78
|
+
avatar_alt: "جين دو - الصورة الشخصية"
|
|
79
|
+
|
|
80
|
+
default_lang: en
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Note:** a page occupying the intended permalink also prevents generation, even if its layout differs. Profile generation does not require a CV URL; CV generation does. Disabled generation or a missing CV URL produces a warning when the corresponding page is absent.
|
|
84
|
+
|
|
85
|
+
Adding a language beyond the six shipped ones: [Add a language](../how-to/add-a-language.md).
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
### 4. Profile Picture / Avatar Settings
|
|
90
|
+
|
|
91
|
+
[`_includes/avatar.html`](../../_includes/avatar.html) renders the avatar. Alt text is per language (`languages.<lang>.avatar_alt`, section 3).
|
|
92
|
+
|
|
93
|
+
| Setting | Type | Default | Description |
|
|
94
|
+
|---|---|---|---|
|
|
95
|
+
| `resume_avatar` | Boolean | unset (hidden) | `true` shows the avatar in the resume header. Must be a Boolean. |
|
|
96
|
+
| `avatar_url` | String | `"/assets/images/Profile-min.jpg"` | Local path (passed through `relative_url`) or external URL (anything containing a scheme, used as-is). Only `http`/`https` schemes are allowed: `javascript:`, `data:`, `file:` and others are a validator error and the avatar is not rendered. The theme does not ship the default file: supply it or set `avatar_url`. |
|
|
97
|
+
| `avatar_link` | String / Boolean | `"/"` | Link destination: a relative path or `http`/`https`/`mailto`/`tel` URL. `false`, or a disallowed scheme, renders a plain `<img>`. |
|
|
98
|
+
| `avatar_link_target` | String | `"_self"` | Link `target` attribute. |
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
resume_avatar: true
|
|
102
|
+
avatar_url: "assets/images/profile.jpg" # or "https://cdn.example.com/avatar.jpg"
|
|
103
|
+
avatar_link: "/"
|
|
104
|
+
avatar_link_target: "_self"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### 5. Contact Information
|
|
110
|
+
|
|
111
|
+
Language-neutral contact details. The address is per language (`languages.<lang>.address`, section 3).
|
|
112
|
+
|
|
113
|
+
| Setting | Type | Default | Description |
|
|
114
|
+
|---|---|---|---|
|
|
115
|
+
| `contact_info.email` | String | `""` | Shown in the contact row and used by the contact button when `resume_looking_for_work: true`. |
|
|
116
|
+
| `contact_info.phone` | String | `""` | Primary phone number. |
|
|
117
|
+
| `contact_info.dob` | Date | unset | Date of birth (`YYYY-MM-DD`), formatted with the locale's month names. |
|
|
118
|
+
| `contact_info.email_live` | String | `""` | Replaces `email` when `enable_live: true`. |
|
|
119
|
+
| `contact_info.phone_live` | String | `""` | Replaces `phone` when `enable_live: true`. |
|
|
120
|
+
|
|
121
|
+
```yaml
|
|
122
|
+
contact_info:
|
|
123
|
+
email: "jane.doe@example.com"
|
|
124
|
+
phone: "+1 555 555 5555"
|
|
125
|
+
dob: 1992-05-14
|
|
126
|
+
# email_live: "live@janedoe.com"
|
|
127
|
+
# phone_live: "+1 555 000 0000"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
### 6. Social Media Links
|
|
133
|
+
|
|
134
|
+
[`_includes/social-links.html`](../../_includes/social-links.html) renders an icon for each configured platform; [`_includes/print-social-links.html`](../../_includes/print-social-links.html) prints the supported platform list as text, labelled from the locale's `ui.social_labels`. Only supported, configured platforms render. `mastodon` currently adds `rel="me"` metadata in the default and profile layouts; it has no social icon or print-list entry yet.
|
|
135
|
+
|
|
136
|
+
`email` renders a `mailto:` link with an accessible label. Every other key takes a full `http`/`https` URL (`whatsapp` is a URL such as `https://wa.me/1234567890`). A value with any other scheme is a validator error and renders no link.
|
|
137
|
+
|
|
138
|
+
```yaml
|
|
139
|
+
social_links:
|
|
140
|
+
email: "jane.doe@example.com"
|
|
141
|
+
github: https://github.com/yourusername
|
|
142
|
+
linkedin: https://www.linkedin.com/in/yourhandle/
|
|
143
|
+
twitter: https://twitter.com/yourhandle
|
|
144
|
+
telegram: https://t.me/yourhandle
|
|
145
|
+
medium: https://medium.com/@yourhandle
|
|
146
|
+
website: https://yourwebsite.com
|
|
147
|
+
whatsapp: https://wa.me/1234567890
|
|
148
|
+
instagram: https://instagram.com/yourhandle
|
|
149
|
+
facebook: https://facebook.com/yourhandle
|
|
150
|
+
youtube: https://youtube.com/@yourhandle
|
|
151
|
+
devto: https://dev.to/yourhandle
|
|
152
|
+
dribbble: https://dribbble.com/yourhandle
|
|
153
|
+
flickr: https://flickr.com/people/yourhandle
|
|
154
|
+
pinterest: https://pinterest.com/yourhandle
|
|
155
|
+
# mastodon emits <link rel="me"> in default.html and profile.html for Fediverse verification:
|
|
156
|
+
mastodon: https://mastodon.social/@yourhandle
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### 7. Resume Display & Behavior Controls
|
|
162
|
+
|
|
163
|
+
| Setting | Type | Default | Description |
|
|
164
|
+
|---|---|---|---|
|
|
165
|
+
| `resume_language_switcher` | Boolean | `true` | Floating switcher listing every other entry in `languages`. `false` hides it; `language_switcher: false` in page front matter hides it on one page. |
|
|
166
|
+
| `display_header_contact_info` | Boolean | unset (hidden) | `true` shows the phone, email, address, and date-of-birth row in the header. |
|
|
167
|
+
| `resume_looking_for_work` | Boolean / omitted | omitted | `true`: contact button; `false`: "not looking" pill; omitted: nothing. |
|
|
168
|
+
| `enable_summary` | Boolean | `false` | Show `summary` fields under roles and courses. |
|
|
169
|
+
| `enable_live` | Boolean | unset | `true` uses `phone_live` and `email_live` instead of `phone` and `email`. An explicit `false` also adds the print-only footer with the page's canonical URL (omitting the key does not). |
|
|
170
|
+
| `resume_print_social_links` | Boolean | unset (hidden) | `true` prints the text list of social links on paper and PDF. |
|
|
171
|
+
|
|
172
|
+
The header intro toggle is per language: `languages.<lang>.header_intro` (section 3).
|
|
173
|
+
|
|
174
|
+
```yaml
|
|
175
|
+
resume_language_switcher: true
|
|
176
|
+
display_header_contact_info: true
|
|
177
|
+
resume_looking_for_work: true
|
|
178
|
+
enable_summary: false
|
|
179
|
+
enable_live: false
|
|
180
|
+
resume_print_social_links: true
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
### 8. Resume Sections Toggle & Order
|
|
186
|
+
|
|
187
|
+
[`_includes/resume-section.html`](../../_includes/resume-section.html) renders one section per entry in `resume_section_order`, for every language. A section branch renders when its `resume_section.<name>` flag is truthy; use YAML booleans. Missing or empty data can leave a section heading without items. Section headings come from the locale's `ui.section_titles`.
|
|
188
|
+
|
|
189
|
+
```yaml
|
|
190
|
+
resume_section:
|
|
191
|
+
experience: true
|
|
192
|
+
education: true
|
|
193
|
+
certifications: true
|
|
194
|
+
courses: true
|
|
195
|
+
volunteering: true
|
|
196
|
+
projects: true
|
|
197
|
+
associations: true
|
|
198
|
+
skills: true
|
|
199
|
+
recognitions: false # plural only; the singular `recognition` key was removed in v1.0.0
|
|
200
|
+
languages: false
|
|
201
|
+
lang_header: true # compact language list in the header (needs display_header_contact_info: true); suppresses the full languages section
|
|
202
|
+
interests: false
|
|
203
|
+
links: false
|
|
204
|
+
publications: false
|
|
205
|
+
references: false
|
|
206
|
+
|
|
207
|
+
resume_section_order:
|
|
208
|
+
- experience
|
|
209
|
+
- education
|
|
210
|
+
- certifications
|
|
211
|
+
- courses
|
|
212
|
+
- volunteering
|
|
213
|
+
- projects
|
|
214
|
+
- associations
|
|
215
|
+
- skills
|
|
216
|
+
- recognitions
|
|
217
|
+
- languages
|
|
218
|
+
- interests
|
|
219
|
+
- links
|
|
220
|
+
- publications
|
|
221
|
+
- references
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
### 9. Styling, Fonts & Dark Mode
|
|
227
|
+
|
|
228
|
+
| Setting | Type | Default | Description |
|
|
229
|
+
|---|---|---|---|
|
|
230
|
+
| `dark_mode` | String / Boolean | `"auto"` | `"auto"`: CSS-only `prefers-color-scheme`, no toggle. `"enabled"` or `true`: floating toggle with `localStorage` persistence. `false`, or any other value such as `"disabled"`: no toggle. |
|
|
231
|
+
| `resume_theme` | String | `"default"` | Added as a `theme-<value>` body class. `no-custom-fonts` also stops web font loading (same effect as `disable_google_fonts: true`). |
|
|
232
|
+
| `disable_google_fonts` | Boolean | `false` | `true` stops the resume layout from loading any locale `font_url` or the default Lora and Open Sans stylesheet. Supply the font yourself, or set `font_family` to a system font in a site locale override. |
|
|
233
|
+
|
|
234
|
+
Page front matter `dark_mode: false` / `true` controls the toggle on one page. These settings do not disable the system-preference CSS or the stored-preference script; `false` hides the button rather than forcing a light palette.
|
|
235
|
+
|
|
236
|
+
Per-language fonts (`font_url`, `font_family`) are locale settings, not `_config.yml` settings: see [Override locale strings](../how-to/override-locale-strings.md). Why dark mode is off in print: [Dark mode approach](../explanation/dark-mode-approach.md).
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### 10. Analytics Configuration
|
|
241
|
+
|
|
242
|
+
Configure at most one provider.
|
|
243
|
+
|
|
244
|
+
| Setting | Type | Description |
|
|
245
|
+
|---|---|---|
|
|
246
|
+
| `analytics.gtm` | String | Google Tag Manager container ID (`"GTM-XXXXXXX"`). Injects the `<head>` script and the `<body>` `<noscript>` iframe. |
|
|
247
|
+
| `analytics.gtag` | String | Google Analytics 4 Measurement ID (`"G-XXXXXXXXXX"`). Injects async `gtag.js`. |
|
|
248
|
+
|
|
249
|
+
```yaml
|
|
250
|
+
analytics:
|
|
251
|
+
# gtm: "GTM-XXXXXXX"
|
|
252
|
+
# gtag: "G-XXXXXXXXXX"
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
### 11. Build-Time Validation
|
|
258
|
+
|
|
259
|
+
The validator runs on every build by default, logs findings, and never fails the build unless strict mode is on. Full reference: [`validator-cli.md`](validator-cli.md).
|
|
260
|
+
|
|
261
|
+
| Setting | Type | Default | Description |
|
|
262
|
+
|---|---|---|---|
|
|
263
|
+
| `validate_resume` | Boolean | on | Set to `false` to skip validation during builds. |
|
|
264
|
+
| `validate_resume_strict` | Boolean | `false` | `true` aborts the build when validation finds errors. |
|
|
265
|
+
| `validate_resume_fail_on_warnings` | Boolean | `false` | With strict mode, also abort on warnings. |
|
|
266
|
+
|
|
267
|
+
```yaml
|
|
268
|
+
# validate_resume: false # opt out
|
|
269
|
+
validate_resume_strict: false
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
### 12. Jekyll Build Settings & Plugins
|
|
275
|
+
|
|
276
|
+
```yaml
|
|
277
|
+
plugins:
|
|
278
|
+
- jekyll-theme-resume
|
|
279
|
+
- jekyll-feed
|
|
280
|
+
- jekyll-seo-tag
|
|
281
|
+
- jekyll-sitemap
|
|
282
|
+
- jekyll-redirect-from
|
|
283
|
+
|
|
284
|
+
include:
|
|
285
|
+
- _redirects
|
|
286
|
+
- .well-known/
|
|
287
|
+
- _pages/
|
|
288
|
+
- _posts/
|
|
289
|
+
|
|
290
|
+
exclude:
|
|
291
|
+
- scratch.md
|
|
292
|
+
- README.md
|
|
293
|
+
- Gemfile*
|
|
294
|
+
- vendor/
|
|
295
|
+
- node_modules/
|
|
296
|
+
- "*.gemspec"
|
|
297
|
+
- netlify.toml
|
|
298
|
+
- vercel.json
|
|
299
|
+
- WARP.md
|
|
300
|
+
- scripts/
|
|
301
|
+
|
|
302
|
+
defaults: []
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
### 13. JSON Resume export
|
|
308
|
+
|
|
309
|
+
Exports are on by default. Every key is optional.
|
|
310
|
+
|
|
311
|
+
| Setting | Type | Default | Description |
|
|
312
|
+
|---|---|---|---|
|
|
313
|
+
| `json_resume.enabled` | Boolean | `true` | `false` disables all exports. |
|
|
314
|
+
| `json_resume.root_export` | Boolean | `true` | `false` disables only the root `/resume.json` copy. |
|
|
315
|
+
| `json_resume.languages` | Array | `[]` | Missing or empty means every key in `site.languages`. A nonempty list is an allowlist; unknown languages are warned about and ignored. A malformed non-array value skips generation. Language codes may contain letters, digits, and internal hyphens or underscores; route separators and traversal characters are rejected. |
|
|
316
|
+
| `json_resume.privacy.export_contact_info` | Boolean | `true` | `false` omits email, phone, location and the WhatsApp profile from the export. With `true`, phone and location are exported only if `display_header_contact_info: true`, and email only if that or `resume_looking_for_work` is `true`. |
|
|
317
|
+
| `social_usernames.<network>` | String | unset | Optional profile username. `social_links` values remain URL strings. |
|
|
318
|
+
|
|
319
|
+
```yaml
|
|
320
|
+
json_resume:
|
|
321
|
+
enabled: true
|
|
322
|
+
root_export: true
|
|
323
|
+
languages: [] # Missing or empty means every key in site.languages
|
|
324
|
+
privacy:
|
|
325
|
+
export_contact_info: true
|
|
326
|
+
|
|
327
|
+
# Optional usernames; existing social_links values remain URL strings.
|
|
328
|
+
social_usernames:
|
|
329
|
+
github: octocat
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Routes, collision rules, visibility, and field mappings: [JSON Resume export configuration and privacy](json-resume-fields.md#configuration).
|
|
333
|
+
|
|
334
|
+
### 14. Content Security Policy
|
|
335
|
+
|
|
336
|
+
The theme ships no CSP header (static sites set headers at the host). It does emit inline code, so a strict `script-src` needs a hash for each inline script, or `'unsafe-inline'`.
|
|
337
|
+
|
|
338
|
+
| Inline code | Where | Directive that governs it |
|
|
339
|
+
|---|---|---|
|
|
340
|
+
| Anti-FOUC theme detector (reads `localStorage`, sets `data-theme`) | `<head>`, every page (`shared-head.html`) | `script-src` |
|
|
341
|
+
| Dark-mode toggle handler | `dark-mode-toggle.html`, when the toggle is enabled | `script-src` |
|
|
342
|
+
| Error-page language detection | `404`/`403`/`500`/`503` pages (`error.html`) | `script-src` |
|
|
343
|
+
| Google Tag Manager or `gtag` snippet | `analytics-head.html`, when `analytics.gtm` / `analytics.gtag` is set | `script-src`, plus `connect-src`/`img-src`/`frame-src` for Google hosts |
|
|
344
|
+
| Locale font and line-height variables | `<style>` in `resume.html` | `style-src` |
|
|
345
|
+
| `onclick="window.location.reload();"` on the error-page Reload button | 500/503 error pages | `script-src-attr` |
|
|
346
|
+
| Some `style="..."` attributes (project and association titles, GTM `<noscript>` iframe) | resume sections, `analytics-body.html` | `style-src-attr` |
|
|
347
|
+
|
|
348
|
+
The language switcher is plain links and needs no script. `<script type="application/json">` blocks are data, not executed, and are not covered by CSP.
|
|
349
|
+
|
|
350
|
+
Starting point without analytics (hashes come from the command below):
|
|
351
|
+
|
|
352
|
+
```text
|
|
353
|
+
Content-Security-Policy:
|
|
354
|
+
default-src 'self';
|
|
355
|
+
script-src 'self' 'sha256-...' 'sha256-...';
|
|
356
|
+
style-src 'self' 'sha256-...';
|
|
357
|
+
style-src-attr 'unsafe-inline';
|
|
358
|
+
script-src-attr 'none';
|
|
359
|
+
img-src 'self' https: data:;
|
|
360
|
+
object-src 'none'; base-uri 'self'; frame-ancestors 'self'
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
- `script-src-attr 'none'` blocks the error-page Reload `onclick`; allow it with `'unsafe-hashes' 'sha256-<hash of window.location.reload();>'` if you keep that button.
|
|
364
|
+
- `style-src-attr 'unsafe-inline'` covers the few inline `style` attributes; removing it needs those attributes moved into CSS.
|
|
365
|
+
- With analytics, add the Google hosts you use (for GA4: `https://www.googletagmanager.com` in `script-src`, `https://*.google-analytics.com` in `connect-src`). Tag Manager can load arbitrary tags, which a strict policy cannot control.
|
|
366
|
+
- A hash changes whenever the inline code changes: the theme's scripts change on upgrade, and the analytics snippet changes with your ID. Regenerate after each upgrade or config change.
|
|
367
|
+
|
|
368
|
+
Generate the hashes from a built site (run in the site root after `jekyll build`; Ruby only, no extra gems):
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
ruby -rdigest -rbase64 -e '
|
|
372
|
+
tags = Dir["_site/**/*.html"].flat_map do |f|
|
|
373
|
+
File.read(f).scan(%r{<(script|style)(?![^>]*\s(?:src|type="application/json"))[^>]*>(.*?)</\1>}m)
|
|
374
|
+
end
|
|
375
|
+
tags.uniq.each { |tag, body| puts "#{tag == "script" ? "script-src" : "style-src"} \x27sha256-#{Base64.strict_encode64(Digest::SHA256.digest(body))}\x27" }'
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Each output line names the directive to add the hash to. Hashes cover the exact bytes between the tags, whitespace included, so serve the files unminified-after-hash or hash the final output.
|