bilingual-jekyll-resume-theme 0.5.2 → 0.5.3
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.
Potentially problematic release.
This version of bilingual-jekyll-resume-theme might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -1
- data/_includes/dark-mode-toggle.html +177 -0
- data/_includes/shared-head.html +18 -0
- data/_layouts/resume-ar.html +4 -0
- data/_layouts/resume-en.html +4 -0
- data/_sass/_all-pages.scss +5 -15
- data/_sass/_base.scss +6 -6
- data/_sass/_dark-mode.scss +365 -0
- data/_sass/_mixins.scss +4 -4
- data/_sass/_profile.scss +14 -9
- data/_sass/_resume-rtl.scss +6 -0
- data/_sass/_resume.scss +11 -11
- data/_sass/_variables.scss +3 -3
- data/assets/css/cv-ar.scss +1 -0
- data/assets/css/cv.scss +1 -0
- data/assets/css/main.scss +1 -0
- data/docs/CONFIG_GUIDE.md +86 -0
- data/docs/INCLUDES_GUIDE.md +31 -0
- data/docs/LAYOUTS_GUIDE.md +6 -1
- data/docs/SASS_GUIDE.md +33 -0
- data/docs/_data/_config.sample.yml +5 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6e8574055be713050db1441e0981a7b13b74baf53ea093a39f25879a41ae059b
|
|
4
|
+
data.tar.gz: 13d1c501de2bc80f4873f07f352d9d592d01e6ee63ed9d359bfc99a8d62f4477
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fa85fa7fd0aa1d84fa61e7b59f5ad3d4927c22bde26f4e1cc77503ccfea4fdfd50c8a05e15ce0417b06b8750d5dc40bdefe6271fee76bd92b3dbedc83430ada6
|
|
7
|
+
data.tar.gz: 1a40a6c189b45e7ebd34683da30c9daf720ed342b6662a3f2c33bfd9dce22869daffd133b604791b2aaaa5ff15b267d6a0f38e4d50e989b91c65c6c71ff1fadf
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
## [0.6.0] - 2026-08-21
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
* Add CLAUDE.md for AI coding assistant guidance and project documentation ([`d68bb03`](https://github.com/kmutahar/bilingual-jekyll-resume-theme/commit/d68bb031043eb88dc7bc6eaaa30a00867f7f7afc))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
* Add dark mode toggle with system preference detection ([`2dcb813`](https://github.com/kmutahar/bilingual-jekyll-resume-theme/commit/2dcb8135817d05f97d8705ea09bfb326d244cd3d))
|
|
15
|
+
|
|
16
|
+
* Enhance CLAUDE.md with comprehensive project documentation and guidance for AI assistants ([`b3facba`](https://github.com/kmutahar/bilingual-jekyll-resume-theme/commit/b3facba6cac2caf853d3f70243dd8a9a2f87ab53))
|
|
17
|
+
|
|
18
|
+
* Revise SECURITY.md with version support and monitoring ([`34c78a0`](https://github.com/kmutahar/bilingual-jekyll-resume-theme/commit/34c78a08ee5e96fd0667b9896c07f6e18e380dd0))
|
|
19
|
+
|
|
20
|
+
* Potential fix for code scanning alert no. 1: Workflow does not contain permissions ([`915f9e7`](https://github.com/kmutahar/bilingual-jekyll-resume-theme/commit/915f9e77982a962e3eda844263d65b5a4ffb849b))
|
|
21
|
+
|
|
7
22
|
## [0.5.2] - 2026-05-09
|
|
8
23
|
|
|
9
24
|
### Added
|
|
@@ -143,7 +158,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
143
158
|
|
|
144
159
|
* Initial commit (New Theme Template) ([`00af662`](https://github.com/kmutahar/bilingual-jekyll-resume-theme/commit/00af6628dfec7aefe0ef7d7083bf98c9713a5ffd))
|
|
145
160
|
|
|
146
|
-
[0.
|
|
161
|
+
[0.6.0]: https://github.com/kmutahar/bilingual-jekyll-resume-theme/compare/v0.5.2...v0.6.0
|
|
162
|
+
[0.5.2]: https://github.com/kmutahar/bilingual-jekyll-resume-theme/compare/v0.5.1...v0.5.2
|
|
147
163
|
[0.5.1]: https://github.com/kmutahar/bilingual-jekyll-resume-theme/compare/v0.5.0...v0.5.1
|
|
148
164
|
[0.5.0]: https://github.com/kmutahar/bilingual-jekyll-resume-theme/compare/v0.4.0...v0.5.0
|
|
149
165
|
[0.4.0]: https://github.com/kmutahar/bilingual-jekyll-resume-theme/compare/v0.3.1...v0.4.0
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<!-- Dark Mode Toggle Button (Two-state: System Default vs Pinned Override) -->
|
|
2
|
+
<button
|
|
3
|
+
id="dark-mode-toggle"
|
|
4
|
+
class="dark-mode-toggle no-print"
|
|
5
|
+
type="button"
|
|
6
|
+
aria-label="Switch to dark mode"
|
|
7
|
+
title="Switch to dark mode"
|
|
8
|
+
aria-pressed="false"
|
|
9
|
+
data-pinned="false"
|
|
10
|
+
>
|
|
11
|
+
<!-- Sun Icon: Visible in dark mode / indicates switching to light mode -->
|
|
12
|
+
<svg
|
|
13
|
+
class="dark-mode-icon dark-mode-icon-sun"
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
width="20"
|
|
16
|
+
height="20"
|
|
17
|
+
viewBox="0 0 24 24"
|
|
18
|
+
fill="none"
|
|
19
|
+
stroke="currentColor"
|
|
20
|
+
stroke-width="2"
|
|
21
|
+
stroke-linecap="round"
|
|
22
|
+
stroke-linejoin="round"
|
|
23
|
+
aria-hidden="true"
|
|
24
|
+
focusable="false"
|
|
25
|
+
>
|
|
26
|
+
<circle cx="12" cy="12" r="5"></circle>
|
|
27
|
+
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
28
|
+
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
29
|
+
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
30
|
+
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
31
|
+
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
32
|
+
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
33
|
+
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
34
|
+
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
35
|
+
</svg>
|
|
36
|
+
|
|
37
|
+
<!-- Moon Icon: Visible in light mode / indicates switching to dark mode -->
|
|
38
|
+
<svg
|
|
39
|
+
class="dark-mode-icon dark-mode-icon-moon"
|
|
40
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
41
|
+
width="20"
|
|
42
|
+
height="20"
|
|
43
|
+
viewBox="0 0 24 24"
|
|
44
|
+
fill="none"
|
|
45
|
+
stroke="currentColor"
|
|
46
|
+
stroke-width="2"
|
|
47
|
+
stroke-linecap="round"
|
|
48
|
+
stroke-linejoin="round"
|
|
49
|
+
aria-hidden="true"
|
|
50
|
+
focusable="false"
|
|
51
|
+
>
|
|
52
|
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
|
53
|
+
</svg>
|
|
54
|
+
</button>
|
|
55
|
+
|
|
56
|
+
<script>
|
|
57
|
+
(function () {
|
|
58
|
+
var toggleBtn = document.getElementById('dark-mode-toggle');
|
|
59
|
+
if (!toggleBtn) return;
|
|
60
|
+
|
|
61
|
+
var metaTheme = document.querySelector('meta[name="color-scheme"]');
|
|
62
|
+
|
|
63
|
+
function getSystemTheme() {
|
|
64
|
+
if (window.matchMedia) {
|
|
65
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
66
|
+
}
|
|
67
|
+
return 'light';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getSavedTheme() {
|
|
71
|
+
try {
|
|
72
|
+
var val = localStorage.getItem('color-scheme');
|
|
73
|
+
if (val === 'dark' || val === 'light') {
|
|
74
|
+
return val;
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
} catch (e) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isArabicRTL() {
|
|
83
|
+
var docEl = document.documentElement;
|
|
84
|
+
var dir = docEl.getAttribute('dir');
|
|
85
|
+
var lang = docEl.getAttribute('lang') || docEl.lang;
|
|
86
|
+
return dir === 'rtl' || lang === 'ar';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function syncState() {
|
|
90
|
+
var saved = getSavedTheme();
|
|
91
|
+
if (saved !== null) {
|
|
92
|
+
document.documentElement.setAttribute('data-color-scheme', saved);
|
|
93
|
+
document.documentElement.setAttribute('data-theme', saved);
|
|
94
|
+
if (metaTheme) {
|
|
95
|
+
metaTheme.setAttribute('content', saved);
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
document.documentElement.removeAttribute('data-color-scheme');
|
|
99
|
+
document.documentElement.removeAttribute('data-theme');
|
|
100
|
+
if (metaTheme) {
|
|
101
|
+
metaTheme.setAttribute('content', 'light dark');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function updateUI() {
|
|
107
|
+
var saved = getSavedTheme();
|
|
108
|
+
var isPinned = (saved !== null);
|
|
109
|
+
var effectiveTheme = isPinned ? saved : getSystemTheme();
|
|
110
|
+
var isRTL = isArabicRTL();
|
|
111
|
+
var label = '';
|
|
112
|
+
|
|
113
|
+
toggleBtn.setAttribute('data-pinned', isPinned ? 'true' : 'false');
|
|
114
|
+
toggleBtn.setAttribute('aria-pressed', isPinned ? 'true' : 'false');
|
|
115
|
+
toggleBtn.setAttribute('data-current-theme', effectiveTheme);
|
|
116
|
+
|
|
117
|
+
if (isPinned) {
|
|
118
|
+
label = isRTL
|
|
119
|
+
? 'إعادة ضبط المظهر إلى الوضع التلقائي للنظام'
|
|
120
|
+
: 'Reset to system theme';
|
|
121
|
+
} else {
|
|
122
|
+
var nextTheme = (effectiveTheme === 'dark') ? 'light' : 'dark';
|
|
123
|
+
if (isRTL) {
|
|
124
|
+
label = (nextTheme === 'dark')
|
|
125
|
+
? 'تبديل إلى الوضع الداكن'
|
|
126
|
+
: 'تبديل إلى الوضع الفاتح';
|
|
127
|
+
} else {
|
|
128
|
+
label = (nextTheme === 'dark')
|
|
129
|
+
? 'Switch to dark mode'
|
|
130
|
+
: 'Switch to light mode';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
toggleBtn.setAttribute('aria-label', label);
|
|
135
|
+
toggleBtn.setAttribute('title', label);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function toggleTheme() {
|
|
139
|
+
var saved = getSavedTheme();
|
|
140
|
+
if (saved !== null) {
|
|
141
|
+
// Currently pinned -> reset to system default
|
|
142
|
+
try {
|
|
143
|
+
localStorage.removeItem('color-scheme');
|
|
144
|
+
} catch (e) {}
|
|
145
|
+
syncState();
|
|
146
|
+
} else {
|
|
147
|
+
// Currently unpinned -> pin opposite of current system setting
|
|
148
|
+
var systemTheme = getSystemTheme();
|
|
149
|
+
var targetTheme = (systemTheme === 'dark') ? 'light' : 'dark';
|
|
150
|
+
try {
|
|
151
|
+
localStorage.setItem('color-scheme', targetTheme);
|
|
152
|
+
} catch (e) {}
|
|
153
|
+
syncState();
|
|
154
|
+
}
|
|
155
|
+
updateUI();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
toggleBtn.addEventListener('click', toggleTheme);
|
|
159
|
+
|
|
160
|
+
if (window.matchMedia) {
|
|
161
|
+
var darkQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
162
|
+
var mqListener = function () {
|
|
163
|
+
if (!getSavedTheme()) {
|
|
164
|
+
updateUI();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
if (darkQuery.addEventListener) {
|
|
168
|
+
darkQuery.addEventListener('change', mqListener);
|
|
169
|
+
} else if (darkQuery.addListener) {
|
|
170
|
+
darkQuery.addListener(mqListener);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
syncState();
|
|
175
|
+
updateUI();
|
|
176
|
+
})();
|
|
177
|
+
</script>
|
data/_includes/shared-head.html
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
<meta charset="UTF-8">
|
|
2
2
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
3
3
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, viewport-fit=cover">
|
|
4
|
+
<meta name="color-scheme" content="light dark">
|
|
5
|
+
<script>
|
|
6
|
+
(function() {
|
|
7
|
+
try {
|
|
8
|
+
var saved = localStorage.getItem('color-scheme');
|
|
9
|
+
if (saved === 'dark' || saved === 'light') {
|
|
10
|
+
document.documentElement.setAttribute('data-color-scheme', saved);
|
|
11
|
+
document.documentElement.setAttribute('data-theme', saved);
|
|
12
|
+
var meta = document.querySelector('meta[name="color-scheme"]');
|
|
13
|
+
if (meta) {
|
|
14
|
+
meta.setAttribute('content', saved);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
} catch (e) {
|
|
18
|
+
// Graceful fallback if localStorage is restricted (private browsing / iframe sandboxing)
|
|
19
|
+
}
|
|
20
|
+
})();
|
|
21
|
+
</script>
|
|
4
22
|
|
|
5
23
|
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
|
|
6
24
|
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
data/_layouts/resume-ar.html
CHANGED
|
@@ -159,6 +159,10 @@ resume_data.experience == site.data['2025-06']['20250621-PM'].experience
|
|
|
159
159
|
<body class="theme-{% if site.resume_theme %}{{ site.resume_theme }}{% endif %}">
|
|
160
160
|
|
|
161
161
|
<div class="wrapper" itemscope itemtype="http://schema.org/Person">
|
|
162
|
+
{% if site.resume_dark_mode == "enabled" %}
|
|
163
|
+
{% include dark-mode-toggle.html %}
|
|
164
|
+
{% endif %}
|
|
165
|
+
|
|
162
166
|
<meta itemprop="telephone" content="{{ site.contact_info.phone }}" />
|
|
163
167
|
<meta itemprop="address" content="{{ site.contact_info.address }}" />
|
|
164
168
|
|
data/_layouts/resume-en.html
CHANGED
|
@@ -159,6 +159,10 @@ resume_data.experience == site.data['2025-06']['20250621-PM'].experience
|
|
|
159
159
|
<body class="theme-{% if site.resume_theme %}{{ site.resume_theme }}{% endif %}">
|
|
160
160
|
|
|
161
161
|
<div class="wrapper" itemscope itemtype="http://schema.org/Person">
|
|
162
|
+
{% if site.resume_dark_mode == "enabled" %}
|
|
163
|
+
{% include dark-mode-toggle.html %}
|
|
164
|
+
{% endif %}
|
|
165
|
+
|
|
162
166
|
<meta itemprop="telephone" content="{{ site.contact_info.phone }}" />
|
|
163
167
|
<meta itemprop="address" content="{{ site.contact_info.address }}" />
|
|
164
168
|
|
data/_sass/_all-pages.scss
CHANGED
|
@@ -2,35 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
.svg-icon,
|
|
4
4
|
svg {
|
|
5
|
-
fill: #BBB;
|
|
5
|
+
fill: var(--icon-fill-muted, #BBB);
|
|
6
6
|
width: 30px;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
.svg-icon:hover,
|
|
10
10
|
svg:hover {
|
|
11
|
-
fill: #000;
|
|
11
|
+
fill: var(--icon-fill-dark, #000);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/* Following only apply to the resume header contact info icons */
|
|
15
15
|
.header-contact-info>p>svg {
|
|
16
16
|
width: 20px;
|
|
17
|
-
fill: #000;
|
|
17
|
+
fill: var(--icon-fill-dark, #000);
|
|
18
18
|
vertical-align: middle;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
/* Footer */
|
|
23
23
|
|
|
24
|
-
/* Old
|
|
25
|
-
footer {
|
|
26
|
-
color: rgba(0, 0, 0, 1);
|
|
27
|
-
width: 100%;
|
|
28
|
-
margin: 50px auto;
|
|
29
|
-
text-align: center;
|
|
30
|
-
flex: none;
|
|
31
|
-
}
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
24
|
footer,
|
|
35
25
|
.page-footer {
|
|
36
26
|
padding-top: 1.5rem;
|
|
@@ -41,11 +31,11 @@ footer,
|
|
|
41
31
|
.copyright {
|
|
42
32
|
margin: 0;
|
|
43
33
|
font-size: .875rem;
|
|
44
|
-
color: #999;
|
|
34
|
+
color: var(--text-muted, #999);
|
|
45
35
|
|
|
46
36
|
>a {
|
|
47
37
|
font-weight: 700;
|
|
48
|
-
color: #333;
|
|
38
|
+
color: var(--link-color, #333);
|
|
49
39
|
}
|
|
50
40
|
}
|
|
51
41
|
}
|
data/_sass/_base.scss
CHANGED
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
html {
|
|
14
|
-
background: variables.$white;
|
|
14
|
+
background: var(--bg-color, variables.$white);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
body {
|
|
18
18
|
@include mixins.serif;
|
|
19
|
-
color: variables.$text_color;
|
|
19
|
+
color: var(--text-color, variables.$text_color);
|
|
20
20
|
font-size: 16px;
|
|
21
21
|
line-height: 1.5;
|
|
22
22
|
}
|
|
@@ -56,13 +56,13 @@ body {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
::-moz-selection {
|
|
59
|
-
background: rgba(51, 51, 51, .8);
|
|
60
|
-
color: #fff;
|
|
59
|
+
background: var(--selection-bg, rgba(51, 51, 51, .8));
|
|
60
|
+
color: var(--selection-color, #fff);
|
|
61
61
|
text-shadow: none;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
::selection {
|
|
65
|
-
background: rgba(51, 51, 51, .8);
|
|
66
|
-
color: #fff;
|
|
65
|
+
background: var(--selection-bg, rgba(51, 51, 51, .8));
|
|
66
|
+
color: var(--selection-color, #fff);
|
|
67
67
|
text-shadow: none;
|
|
68
68
|
}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// Dark Mode System Tokens & Overrides
|
|
3
|
+
// bilingual-jekyll-resume-theme
|
|
4
|
+
// ==========================================================================
|
|
5
|
+
|
|
6
|
+
:root {
|
|
7
|
+
color-scheme: light dark;
|
|
8
|
+
|
|
9
|
+
// --- Background & Primary Typography ---
|
|
10
|
+
--bg-color: #ffffff;
|
|
11
|
+
--text-color: #333;
|
|
12
|
+
--text-muted: #999;
|
|
13
|
+
--text-light: #646464;
|
|
14
|
+
--border-color: #c7c7c7;
|
|
15
|
+
--card-bg: #efefef;
|
|
16
|
+
|
|
17
|
+
// --- Links & Navigation ---
|
|
18
|
+
--link-color: #333;
|
|
19
|
+
--link-hover: #9c9c9c;
|
|
20
|
+
--link-hover-color: var(--link-hover);
|
|
21
|
+
|
|
22
|
+
// --- Accent & Brand Colors ---
|
|
23
|
+
--accent-color: #3064a9;
|
|
24
|
+
--accent-hover: #307EA9;
|
|
25
|
+
--accent-hover-color: var(--accent-hover);
|
|
26
|
+
--social-hover-color: var(--accent-hover);
|
|
27
|
+
--about-color: var(--text-light);
|
|
28
|
+
|
|
29
|
+
// --- Footer ---
|
|
30
|
+
--footer-text-color: var(--text-muted);
|
|
31
|
+
--footer-link-color: var(--link-color);
|
|
32
|
+
|
|
33
|
+
// --- Icons & Graphics ---
|
|
34
|
+
--icon-fill: #333;
|
|
35
|
+
--icon-fill-muted: #BBB;
|
|
36
|
+
--icon-fill-dark: #000;
|
|
37
|
+
--icon-hover-fill: var(--icon-fill-dark);
|
|
38
|
+
--header-icon-fill: var(--icon-fill-dark);
|
|
39
|
+
|
|
40
|
+
// --- Buttons (.contact-button, .cv-button) ---
|
|
41
|
+
--button-bg: #efefef;
|
|
42
|
+
--button-text: #333;
|
|
43
|
+
--button-hover-bg: #333;
|
|
44
|
+
--button-hover-text: #fff;
|
|
45
|
+
|
|
46
|
+
// --- Text Selection ---
|
|
47
|
+
--selection-bg: rgba(51, 51, 51, .8);
|
|
48
|
+
--selection-color: #fff;
|
|
49
|
+
|
|
50
|
+
// --- Dark Mode Toggle Component Tokens ---
|
|
51
|
+
--toggle-btn-bg: transparent;
|
|
52
|
+
--toggle-btn-border: var(--border-color);
|
|
53
|
+
--toggle-btn-color: var(--text-color);
|
|
54
|
+
--toggle-btn-hover-bg: var(--button-bg);
|
|
55
|
+
--toggle-btn-focus-ring: var(--accent-color);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// --------------------------------------------------------------------------
|
|
59
|
+
// Automatic System Dark Mode (@media prefers-color-scheme)
|
|
60
|
+
// --------------------------------------------------------------------------
|
|
61
|
+
@media (prefers-color-scheme: dark) {
|
|
62
|
+
:root:not([data-color-scheme="light"]):not([data-theme="light"]) {
|
|
63
|
+
--bg-color: #121212;
|
|
64
|
+
--text-color: #e0e0e0;
|
|
65
|
+
--text-muted: #888888;
|
|
66
|
+
--text-light: #aaaaaa;
|
|
67
|
+
--border-color: #333333;
|
|
68
|
+
--card-bg: #1e1e1e;
|
|
69
|
+
|
|
70
|
+
--link-color: #e0e0e0;
|
|
71
|
+
--link-hover: #ffffff;
|
|
72
|
+
--link-hover-color: var(--link-hover);
|
|
73
|
+
|
|
74
|
+
--accent-color: #6ba4e8;
|
|
75
|
+
--accent-hover: #8cbcf3;
|
|
76
|
+
--accent-hover-color: var(--accent-hover);
|
|
77
|
+
--social-hover-color: var(--accent-hover);
|
|
78
|
+
--about-color: var(--text-light);
|
|
79
|
+
|
|
80
|
+
--footer-text-color: var(--text-muted);
|
|
81
|
+
--footer-link-color: var(--link-color);
|
|
82
|
+
|
|
83
|
+
--icon-fill: #e0e0e0;
|
|
84
|
+
--icon-fill-muted: #666666;
|
|
85
|
+
--icon-fill-dark: #ffffff;
|
|
86
|
+
--icon-hover-fill: var(--icon-fill-dark);
|
|
87
|
+
--header-icon-fill: var(--icon-fill-dark);
|
|
88
|
+
|
|
89
|
+
--button-bg: #2a2a2a;
|
|
90
|
+
--button-text: #e0e0e0;
|
|
91
|
+
--button-hover-bg: #444444;
|
|
92
|
+
--button-hover-text: #ffffff;
|
|
93
|
+
|
|
94
|
+
--selection-bg: rgba(107, 164, 232, .5);
|
|
95
|
+
--selection-color: #ffffff;
|
|
96
|
+
|
|
97
|
+
--toggle-btn-border: var(--border-color);
|
|
98
|
+
--toggle-btn-color: var(--text-color);
|
|
99
|
+
--toggle-btn-hover-bg: var(--button-bg);
|
|
100
|
+
--toggle-btn-focus-ring: var(--accent-color);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// --------------------------------------------------------------------------
|
|
105
|
+
// Forced Dark Override (Pinned via toggle or explicit attribute)
|
|
106
|
+
// --------------------------------------------------------------------------
|
|
107
|
+
:root[data-color-scheme="dark"],
|
|
108
|
+
:root[data-theme="dark"],
|
|
109
|
+
[data-color-scheme="dark"],
|
|
110
|
+
[data-theme="dark"],
|
|
111
|
+
html.dark {
|
|
112
|
+
color-scheme: dark;
|
|
113
|
+
--bg-color: #121212;
|
|
114
|
+
--text-color: #e0e0e0;
|
|
115
|
+
--text-muted: #888888;
|
|
116
|
+
--text-light: #aaaaaa;
|
|
117
|
+
--border-color: #333333;
|
|
118
|
+
--card-bg: #1e1e1e;
|
|
119
|
+
|
|
120
|
+
--link-color: #e0e0e0;
|
|
121
|
+
--link-hover: #ffffff;
|
|
122
|
+
--link-hover-color: var(--link-hover);
|
|
123
|
+
|
|
124
|
+
--accent-color: #6ba4e8;
|
|
125
|
+
--accent-hover: #8cbcf3;
|
|
126
|
+
--accent-hover-color: var(--accent-hover);
|
|
127
|
+
--social-hover-color: var(--accent-hover);
|
|
128
|
+
--about-color: var(--text-light);
|
|
129
|
+
|
|
130
|
+
--footer-text-color: var(--text-muted);
|
|
131
|
+
--footer-link-color: var(--link-color);
|
|
132
|
+
|
|
133
|
+
--icon-fill: #e0e0e0;
|
|
134
|
+
--icon-fill-muted: #666666;
|
|
135
|
+
--icon-fill-dark: #ffffff;
|
|
136
|
+
--icon-hover-fill: var(--icon-fill-dark);
|
|
137
|
+
--header-icon-fill: var(--icon-fill-dark);
|
|
138
|
+
|
|
139
|
+
--button-bg: #2a2a2a;
|
|
140
|
+
--button-text: #e0e0e0;
|
|
141
|
+
--button-hover-bg: #444444;
|
|
142
|
+
--button-hover-text: #ffffff;
|
|
143
|
+
|
|
144
|
+
--selection-bg: rgba(107, 164, 232, .5);
|
|
145
|
+
--selection-color: #ffffff;
|
|
146
|
+
|
|
147
|
+
--toggle-btn-border: var(--border-color);
|
|
148
|
+
--toggle-btn-color: var(--text-color);
|
|
149
|
+
--toggle-btn-hover-bg: var(--button-bg);
|
|
150
|
+
--toggle-btn-focus-ring: var(--accent-color);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// --------------------------------------------------------------------------
|
|
154
|
+
// Forced Light Override (Pinned via toggle on dark OS)
|
|
155
|
+
// --------------------------------------------------------------------------
|
|
156
|
+
:root[data-color-scheme="light"],
|
|
157
|
+
:root[data-theme="light"],
|
|
158
|
+
[data-color-scheme="light"],
|
|
159
|
+
[data-theme="light"],
|
|
160
|
+
html.light {
|
|
161
|
+
color-scheme: light;
|
|
162
|
+
--bg-color: #ffffff;
|
|
163
|
+
--text-color: #333;
|
|
164
|
+
--text-muted: #999;
|
|
165
|
+
--text-light: #646464;
|
|
166
|
+
--border-color: #c7c7c7;
|
|
167
|
+
--card-bg: #efefef;
|
|
168
|
+
|
|
169
|
+
--link-color: #333;
|
|
170
|
+
--link-hover: #9c9c9c;
|
|
171
|
+
--link-hover-color: var(--link-hover);
|
|
172
|
+
|
|
173
|
+
--accent-color: #3064a9;
|
|
174
|
+
--accent-hover: #307EA9;
|
|
175
|
+
--accent-hover-color: var(--accent-hover);
|
|
176
|
+
--social-hover-color: var(--accent-hover);
|
|
177
|
+
--about-color: var(--text-light);
|
|
178
|
+
|
|
179
|
+
--footer-text-color: var(--text-muted);
|
|
180
|
+
--footer-link-color: var(--link-color);
|
|
181
|
+
|
|
182
|
+
--icon-fill: #333;
|
|
183
|
+
--icon-fill-muted: #BBB;
|
|
184
|
+
--icon-fill-dark: #000;
|
|
185
|
+
--icon-hover-fill: var(--icon-fill-dark);
|
|
186
|
+
--header-icon-fill: var(--icon-fill-dark);
|
|
187
|
+
|
|
188
|
+
--button-bg: #efefef;
|
|
189
|
+
--button-text: #333;
|
|
190
|
+
--button-hover-bg: #333;
|
|
191
|
+
--button-hover-text: #fff;
|
|
192
|
+
|
|
193
|
+
--selection-bg: rgba(51, 51, 51, .8);
|
|
194
|
+
--selection-color: #fff;
|
|
195
|
+
|
|
196
|
+
--toggle-btn-border: var(--border-color);
|
|
197
|
+
--toggle-btn-color: var(--text-color);
|
|
198
|
+
--toggle-btn-hover-bg: var(--button-bg);
|
|
199
|
+
--toggle-btn-focus-ring: var(--accent-color);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// --------------------------------------------------------------------------
|
|
203
|
+
// Print Media Override (Always clean light background for printing)
|
|
204
|
+
// --------------------------------------------------------------------------
|
|
205
|
+
@media print {
|
|
206
|
+
:root,
|
|
207
|
+
:root[data-color-scheme="dark"],
|
|
208
|
+
:root[data-color-scheme="light"],
|
|
209
|
+
:root[data-theme="dark"],
|
|
210
|
+
:root[data-theme="light"],
|
|
211
|
+
[data-color-scheme="dark"],
|
|
212
|
+
[data-theme="dark"],
|
|
213
|
+
html.dark,
|
|
214
|
+
html.light {
|
|
215
|
+
color-scheme: light !important;
|
|
216
|
+
--bg-color: #ffffff !important;
|
|
217
|
+
--text-color: #000000 !important;
|
|
218
|
+
--text-muted: #666 !important;
|
|
219
|
+
--text-light: #444 !important;
|
|
220
|
+
--border-color: #c7c7c7 !important;
|
|
221
|
+
--card-bg: #fff !important;
|
|
222
|
+
|
|
223
|
+
--link-color: #000 !important;
|
|
224
|
+
--link-hover: #000 !important;
|
|
225
|
+
--link-hover-color: #000 !important;
|
|
226
|
+
|
|
227
|
+
--accent-color: #000 !important;
|
|
228
|
+
--accent-hover: #000 !important;
|
|
229
|
+
--accent-hover-color: #000 !important;
|
|
230
|
+
--social-hover-color: #000 !important;
|
|
231
|
+
--about-color: #444 !important;
|
|
232
|
+
|
|
233
|
+
--footer-text-color: #666 !important;
|
|
234
|
+
--footer-link-color: #000 !important;
|
|
235
|
+
|
|
236
|
+
--icon-fill: #000 !important;
|
|
237
|
+
--icon-fill-muted: #666 !important;
|
|
238
|
+
--icon-fill-dark: #000 !important;
|
|
239
|
+
--icon-hover-fill: #000 !important;
|
|
240
|
+
--header-icon-fill: #000 !important;
|
|
241
|
+
|
|
242
|
+
--button-bg: #fff !important;
|
|
243
|
+
--button-text: #000 !important;
|
|
244
|
+
--button-hover-bg: #fff !important;
|
|
245
|
+
--button-hover-text: #000 !important;
|
|
246
|
+
|
|
247
|
+
--selection-bg: transparent !important;
|
|
248
|
+
--selection-color: #000 !important;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
html,
|
|
252
|
+
body {
|
|
253
|
+
background-color: #fff !important;
|
|
254
|
+
color: #000 !important;
|
|
255
|
+
-webkit-print-color-adjust: exact;
|
|
256
|
+
print-color-adjust: exact;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.dark-mode-toggle,
|
|
260
|
+
#dark-mode-toggle {
|
|
261
|
+
display: none !important;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// --------------------------------------------------------------------------
|
|
266
|
+
// Toggle Button Component Styling
|
|
267
|
+
// --------------------------------------------------------------------------
|
|
268
|
+
.dark-mode-toggle {
|
|
269
|
+
position: fixed;
|
|
270
|
+
top: 1.25rem;
|
|
271
|
+
right: 1.25rem;
|
|
272
|
+
z-index: 1000;
|
|
273
|
+
display: inline-flex;
|
|
274
|
+
align-items: center;
|
|
275
|
+
justify-content: center;
|
|
276
|
+
width: 2.5rem;
|
|
277
|
+
height: 2.5rem;
|
|
278
|
+
padding: 0;
|
|
279
|
+
background: var(--bg-color);
|
|
280
|
+
color: var(--toggle-btn-color);
|
|
281
|
+
border: 1px solid var(--toggle-btn-border);
|
|
282
|
+
border-radius: 50%;
|
|
283
|
+
cursor: pointer;
|
|
284
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
285
|
+
transition: background-color 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
|
|
286
|
+
|
|
287
|
+
&:hover {
|
|
288
|
+
background-color: var(--toggle-btn-hover-bg);
|
|
289
|
+
transform: scale(1.05);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
&:focus-visible {
|
|
293
|
+
outline: 2px solid var(--toggle-btn-focus-ring);
|
|
294
|
+
outline-offset: 2px;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.dark-mode-icon {
|
|
298
|
+
width: 1.25rem;
|
|
299
|
+
height: 1.25rem;
|
|
300
|
+
stroke: currentColor;
|
|
301
|
+
transition: transform 0.3s ease;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Baseline Light Mode: Show Moon, Hide Sun
|
|
305
|
+
.dark-mode-icon-sun {
|
|
306
|
+
display: none;
|
|
307
|
+
}
|
|
308
|
+
.dark-mode-icon-moon {
|
|
309
|
+
display: block;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Media Query System Dark Mode (when not overridden to Light)
|
|
314
|
+
@media (prefers-color-scheme: dark) {
|
|
315
|
+
:root:not([data-color-scheme="light"]):not([data-theme="light"]) {
|
|
316
|
+
.dark-mode-toggle {
|
|
317
|
+
.dark-mode-icon-sun {
|
|
318
|
+
display: block;
|
|
319
|
+
}
|
|
320
|
+
.dark-mode-icon-moon {
|
|
321
|
+
display: none;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Pinned Dark Override: Show Sun, Hide Moon
|
|
328
|
+
:root[data-color-scheme="dark"],
|
|
329
|
+
:root[data-theme="dark"],
|
|
330
|
+
[data-color-scheme="dark"],
|
|
331
|
+
[data-theme="dark"],
|
|
332
|
+
html.dark {
|
|
333
|
+
.dark-mode-toggle {
|
|
334
|
+
.dark-mode-icon-sun {
|
|
335
|
+
display: block;
|
|
336
|
+
}
|
|
337
|
+
.dark-mode-icon-moon {
|
|
338
|
+
display: none;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Pinned Light Override: Show Moon, Hide Sun
|
|
344
|
+
:root[data-color-scheme="light"],
|
|
345
|
+
:root[data-theme="light"],
|
|
346
|
+
[data-color-scheme="light"],
|
|
347
|
+
[data-theme="light"],
|
|
348
|
+
html.light {
|
|
349
|
+
.dark-mode-toggle {
|
|
350
|
+
.dark-mode-icon-sun {
|
|
351
|
+
display: none;
|
|
352
|
+
}
|
|
353
|
+
.dark-mode-icon-moon {
|
|
354
|
+
display: block;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// RTL layout positioning
|
|
360
|
+
html[dir="rtl"] {
|
|
361
|
+
.dark-mode-toggle {
|
|
362
|
+
right: auto;
|
|
363
|
+
left: 1.25rem;
|
|
364
|
+
}
|
|
365
|
+
}
|
data/_sass/_mixins.scss
CHANGED
|
@@ -99,13 +99,13 @@
|
|
|
99
99
|
// layout
|
|
100
100
|
|
|
101
101
|
@mixin section_border {
|
|
102
|
-
border-top: 4px solid #c7c7c7;
|
|
103
|
-
border-bottom: 2px solid #c7c7c7;
|
|
102
|
+
border-top: 4px solid var(--border-color, #c7c7c7);
|
|
103
|
+
border-bottom: 2px solid var(--border-color, #c7c7c7);
|
|
104
104
|
padding: .2rem 0 .4rem;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
@mixin section_border_thin {
|
|
108
|
-
border-top: 1px solid #c7c7c7;
|
|
109
|
-
border-bottom: 1px solid #c7c7c7;
|
|
108
|
+
border-top: 1px solid var(--border-color, #c7c7c7);
|
|
109
|
+
border-bottom: 1px solid var(--border-color, #c7c7c7);
|
|
110
110
|
padding: .2rem 0 .2rem;
|
|
111
111
|
}
|
data/_sass/_profile.scss
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
html {
|
|
2
|
+
background-color: var(--bg-color, #fff);
|
|
3
|
+
}
|
|
4
|
+
|
|
1
5
|
body {
|
|
2
|
-
color: #
|
|
6
|
+
background-color: var(--bg-color, #fff);
|
|
7
|
+
color: var(--text-color, #333);
|
|
3
8
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
4
9
|
line-height: 1;
|
|
5
10
|
margin: 10% 0 8%;
|
|
@@ -24,7 +29,7 @@ body {
|
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
.about {
|
|
27
|
-
color: #646464;
|
|
32
|
+
color: var(--text-light, #646464);
|
|
28
33
|
font-size: 87.5%;
|
|
29
34
|
padding-bottom: 14px;
|
|
30
35
|
line-height: .6cm;
|
|
@@ -38,7 +43,7 @@ body {
|
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
a:hover {
|
|
41
|
-
color: #3064a9;
|
|
46
|
+
color: var(--accent-color, #3064a9);
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
.social-links li {
|
|
@@ -47,16 +52,16 @@ a:hover {
|
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
.social-links li a {
|
|
50
|
-
color: #BBB;
|
|
55
|
+
color: var(--icon-fill-muted, #BBB);
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
.social-links li a:hover {
|
|
54
|
-
color: #307EA9;
|
|
59
|
+
color: var(--accent-hover, #307EA9);
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
.cv-button {
|
|
58
63
|
border-radius: 3px;
|
|
59
|
-
background-color: #efefef;
|
|
64
|
+
background-color: var(--button-bg, #efefef);
|
|
60
65
|
text-decoration: none;
|
|
61
66
|
text-align: center;
|
|
62
67
|
display: block;
|
|
@@ -64,12 +69,12 @@ a:hover {
|
|
|
64
69
|
width: 220px;
|
|
65
70
|
font-size: 1.375rem;
|
|
66
71
|
font-weight: 300;
|
|
67
|
-
color: #333;
|
|
72
|
+
color: var(--button-text, #333);
|
|
68
73
|
line-height: 55px;
|
|
69
74
|
transition: (all .2s ease);
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
.cv-button:hover {
|
|
73
|
-
background-color: #333;
|
|
74
|
-
color: #fff;
|
|
78
|
+
background-color: var(--button-hover-bg, #333);
|
|
79
|
+
color: var(--button-hover-text, #fff);
|
|
75
80
|
}
|
data/_sass/_resume-rtl.scss
CHANGED
data/_sass/_resume.scss
CHANGED
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
position: relative;
|
|
89
89
|
top: 4px;
|
|
90
90
|
text-align: center;
|
|
91
|
-
border-top: 2px solid #c7c7c7;
|
|
91
|
+
border-top: 2px solid var(--border-color, #c7c7c7);
|
|
92
92
|
padding-top: .4rem;
|
|
93
93
|
|
|
94
94
|
@include mixins.media_larger_than_mobile {
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
display: inline-block;
|
|
107
107
|
|
|
108
108
|
&:hover .icon path {
|
|
109
|
-
fill: #333;
|
|
109
|
+
fill: var(--icon-fill, #333);
|
|
110
110
|
@include mixins.transition(all .2s ease);
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
|
|
119
119
|
.contact-button {
|
|
120
120
|
@include mixins.border-radius(3px);
|
|
121
|
-
background-color: #efefef;
|
|
121
|
+
background-color: var(--button-bg, #efefef);
|
|
122
122
|
text-decoration: none;
|
|
123
123
|
text-align: center;
|
|
124
124
|
display: block;
|
|
@@ -126,13 +126,13 @@
|
|
|
126
126
|
width: 220px;
|
|
127
127
|
font-size: 1.375rem;
|
|
128
128
|
@include mixins.sans_light;
|
|
129
|
-
color: #333;
|
|
129
|
+
color: var(--button-text, #333);
|
|
130
130
|
line-height: 55px;
|
|
131
131
|
@include mixins.transition(all .2s ease);
|
|
132
132
|
|
|
133
133
|
&:hover {
|
|
134
|
-
background-color: #333;
|
|
135
|
-
color: #fff;
|
|
134
|
+
background-color: var(--button-hover-bg, #333);
|
|
135
|
+
color: var(--button-hover-text, #fff);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
&.not-looking {
|
|
@@ -172,13 +172,13 @@
|
|
|
172
172
|
.resume-item-copy,
|
|
173
173
|
.resume-item-list {
|
|
174
174
|
a {
|
|
175
|
-
color: #333;
|
|
175
|
+
color: var(--link-color, #333);
|
|
176
176
|
text-decoration: none;
|
|
177
177
|
border-bottom: 1px solid;
|
|
178
178
|
|
|
179
179
|
&:hover {
|
|
180
180
|
border-bottom: none;
|
|
181
|
-
color: #9c9c9c;
|
|
181
|
+
color: var(--link-hover, #9c9c9c);
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
}
|
|
@@ -261,7 +261,7 @@
|
|
|
261
261
|
// footer styles
|
|
262
262
|
|
|
263
263
|
.page-footer {
|
|
264
|
-
border-top: 4px solid #c7c7c7;
|
|
264
|
+
border-top: 4px solid var(--border-color, #c7c7c7);
|
|
265
265
|
padding-top: 1.5rem;
|
|
266
266
|
text-align: center;
|
|
267
267
|
margin-bottom: 3rem;
|
|
@@ -269,11 +269,11 @@
|
|
|
269
269
|
.footer-line {
|
|
270
270
|
margin: 0;
|
|
271
271
|
font-size: .875rem;
|
|
272
|
-
color: #999;
|
|
272
|
+
color: var(--text-muted, #999);
|
|
273
273
|
|
|
274
274
|
>a {
|
|
275
275
|
font-weight: 700;
|
|
276
|
-
color: #333;
|
|
276
|
+
color: var(--link-color, #333);
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
}
|
data/_sass/_variables.scss
CHANGED
|
@@ -5,11 +5,11 @@ $container-width: 980px !default;
|
|
|
5
5
|
$grid-gutter: 10px !default;
|
|
6
6
|
|
|
7
7
|
// Colors
|
|
8
|
-
$white: #fff;
|
|
9
|
-
$black: #333;
|
|
8
|
+
$white: var(--bg-color, #fff);
|
|
9
|
+
$black: var(--text-color, #333);
|
|
10
10
|
|
|
11
11
|
// Styles
|
|
12
|
-
$text_color:
|
|
12
|
+
$text_color: var(--text-color, #333);
|
|
13
13
|
|
|
14
14
|
// Font stack
|
|
15
15
|
$body-font: Helvetica,
|
data/assets/css/cv-ar.scss
CHANGED
data/assets/css/cv.scss
CHANGED
data/assets/css/main.scss
CHANGED
data/docs/CONFIG_GUIDE.md
CHANGED
|
@@ -16,6 +16,7 @@ Make your site render correctly with this theme. This guide matches the shipped
|
|
|
16
16
|
- [Social links](#social-links)
|
|
17
17
|
- [Analytics](#analytics)
|
|
18
18
|
- [Design and authors](#design-and-authors)
|
|
19
|
+
- [Dark Mode](#dark-mode)
|
|
19
20
|
- [Jekyll build settings](#jekyll-build-settings)
|
|
20
21
|
- [Full example config](#full-example-config)
|
|
21
22
|
- [FAQs](#faqs)
|
|
@@ -193,6 +194,86 @@ Choose one (do not enable both):
|
|
|
193
194
|
|
|
194
195
|
---
|
|
195
196
|
|
|
197
|
+
## Dark Mode
|
|
198
|
+
|
|
199
|
+
The theme features built-in, accessible dark mode support with automatic system preference detection via `prefers-color-scheme` and an optional interactive two-state manual toggle.
|
|
200
|
+
|
|
201
|
+
### Configuration (`resume_dark_mode`)
|
|
202
|
+
|
|
203
|
+
Configure dark mode behavior in your `_config.yml` using the `resume_dark_mode` option:
|
|
204
|
+
|
|
205
|
+
```yaml
|
|
206
|
+
# Dark mode configuration (options: auto | enabled)
|
|
207
|
+
resume_dark_mode: auto
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
#### Accepted Values
|
|
211
|
+
|
|
212
|
+
- **`auto`** (default):
|
|
213
|
+
- Pure CSS-first system detection via `@media (prefers-color-scheme: dark)` and `:root { color-scheme: light dark; }`.
|
|
214
|
+
- Automatically switches between light and dark palettes to match the visitor's operating system / browser theme.
|
|
215
|
+
- **No toggle button is rendered in the HTML**, resulting in zero JavaScript runtime overhead and no layout shifts.
|
|
216
|
+
- If `resume_dark_mode` is omitted or not defined, the theme safely defaults to `auto`.
|
|
217
|
+
|
|
218
|
+
- **`enabled`**:
|
|
219
|
+
- Full automatic system detection **plus** an interactive floating toggle button rendered on resume pages (`resume-en.html` and `resume-ar.html`).
|
|
220
|
+
- Allows visitors to manually override their system color scheme.
|
|
221
|
+
- Preference is persisted across visits and pages via `localStorage`.
|
|
222
|
+
|
|
223
|
+
### Two-State Toggle & State Machine
|
|
224
|
+
|
|
225
|
+
When `resume_dark_mode: enabled` is configured, the toggle operates as a predictable two-state finite state machine (FSM):
|
|
226
|
+
|
|
227
|
+
1. **State A: System Default (Unpinned)**
|
|
228
|
+
- Initial state when no preference is stored in `localStorage` (`color-scheme` key is `null`).
|
|
229
|
+
- The theme strictly follows the visitor's OS setting (`prefers-color-scheme`).
|
|
230
|
+
- Live media query listeners (`window.matchMedia`) dynamically update toggle accessibility labels if the OS theme shifts during the session.
|
|
231
|
+
- Clicking the toggle transitions to **State B (Pinned)**, forcing the *opposite* of the active system theme (e.g., clicking on a light OS pins dark mode).
|
|
232
|
+
|
|
233
|
+
2. **State B: Forced Override (Pinned)**
|
|
234
|
+
- The chosen scheme (`"dark"` or `"light"`) is saved in `localStorage` under the key `color-scheme`.
|
|
235
|
+
- The `<html>` element receives `data-color-scheme` and `data-theme` attributes, and `<meta name="color-scheme">` is updated to match.
|
|
236
|
+
- Clicking the toggle again clears the `localStorage` key and transitions back to **State A (System Default)**.
|
|
237
|
+
|
|
238
|
+
### Anti-FOUC (Flash of Unstyled Content) Prevention
|
|
239
|
+
|
|
240
|
+
To eliminate any visual flash or layout shift for returning visitors:
|
|
241
|
+
- A synchronous, non-deferred `<script>` tag is executed in the document `<head>` (via `_includes/shared-head.html`) immediately after `<meta name="color-scheme" content="light dark">`.
|
|
242
|
+
- It reads `localStorage.getItem('color-scheme')` before DOM rendering starts and synchronously applies `data-color-scheme` / `data-theme` to the root element.
|
|
243
|
+
|
|
244
|
+
### Bilingual Accessibility & RTL Parity
|
|
245
|
+
|
|
246
|
+
The toggle component is designed for WCAG 2.1 AA compliance:
|
|
247
|
+
- **Keyboard navigation**: Fully focusable with a high-contrast `:focus-visible` focus ring; toggles via <kbd>Enter</kbd> or <kbd>Space</kbd>.
|
|
248
|
+
- **Screen readers**: Dynamic `aria-label` and `title` attributes announce the current action and target theme state in both English (LTR) and Arabic (RTL):
|
|
249
|
+
- **English (LTR)**:
|
|
250
|
+
- Unpinned (System Light): `"Switch to dark mode"`
|
|
251
|
+
- Unpinned (System Dark): `"Switch to light mode"`
|
|
252
|
+
- Pinned: `"Reset to system theme"`
|
|
253
|
+
- **Arabic (RTL)**:
|
|
254
|
+
- Unpinned (System Light): `"تبديل إلى الوضع الداكن"`
|
|
255
|
+
- Unpinned (System Dark): `"تبديل إلى الوضع الفاتح"`
|
|
256
|
+
- Pinned: `"إعادة ضبط المظهر إلى الوضع التلقائي للنظام"`
|
|
257
|
+
- **RTL positioning**: In Arabic layouts (`resume-ar.html`), the toggle automatically repositions to the top-left (`left: 1.5rem`) to maintain balanced spacing relative to RTL content.
|
|
258
|
+
|
|
259
|
+
### Print Media Behavior
|
|
260
|
+
|
|
261
|
+
When printing or exporting to PDF:
|
|
262
|
+
- The toggle button is automatically hidden via the `.no-print` class and `@media print { display: none !important; }`.
|
|
263
|
+
- Print styles enforce a clean white background (`--bg-color: #ffffff !important`) and crisp black text (`--text-color: #000000 !important`), overriding both dark mode and manual pins.
|
|
264
|
+
|
|
265
|
+
### Liquid Integration Example
|
|
266
|
+
|
|
267
|
+
Theme layouts include the toggle conditionally using Liquid:
|
|
268
|
+
|
|
269
|
+
```liquid
|
|
270
|
+
{% if site.resume_dark_mode == "enabled" %}
|
|
271
|
+
{% include dark-mode-toggle.html %}
|
|
272
|
+
{% endif %}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
196
277
|
## Jekyll build settings
|
|
197
278
|
|
|
198
279
|
- plugins (required): jekyll-feed, jekyll-seo-tag, jekyll-sitemap, jekyll-redirect-from
|
|
@@ -291,6 +372,7 @@ social_links:
|
|
|
291
372
|
# Theme & analytics
|
|
292
373
|
authors: []
|
|
293
374
|
resume_theme: default
|
|
375
|
+
# resume_dark_mode: auto # Options: auto (default: CSS system detection), enabled (manual toggle button)
|
|
294
376
|
analytics:
|
|
295
377
|
# gtm: GTM-XXXXXXX
|
|
296
378
|
# ga: true
|
|
@@ -334,3 +416,7 @@ exclude:
|
|
|
334
416
|
- Enable `resume_header_intro_en: true` or `resume_header_intro_ar: true` in `_config.yml`, and create `_data/en/header.yml` (or `_data/ar/header.yml` for Arabic) in your active data path with an `intro:` field. You can copy sample files from `docs/_data/en/header.yml` and `docs/_data/ar/header.yml`.
|
|
335
417
|
- Data for EN/AR lives in different folders.
|
|
336
418
|
- Point `active_resume_path_en` and `active_resume_path_ar` at the right subtrees (e.g., `en` and `ar`).
|
|
419
|
+
- How do I enable the interactive dark mode toggle?
|
|
420
|
+
- Set `resume_dark_mode: enabled` in your `_config.yml`. By default (`auto` or omitted), dark mode adapts automatically to the user's OS preference without rendering a toggle button.
|
|
421
|
+
- Why doesn't dark mode apply when printing or saving to PDF?
|
|
422
|
+
- Resumes are optimized for clean physical and PDF printing with high-contrast black text on a pure white background. Dark mode tokens and the toggle button are automatically suppressed in print stylesheets.
|
data/docs/INCLUDES_GUIDE.md
CHANGED
|
@@ -24,6 +24,7 @@ This guide assumes basic familiarity with Jekyll and Liquid, but includes exampl
|
|
|
24
24
|
- [social-links.html](#9-social-linkshtml)
|
|
25
25
|
- [print-social-links.html](#10-print-social-linkshtml)
|
|
26
26
|
- [vendors/ (SVG icon packs)](#11-vendors-svg-icon-packs)
|
|
27
|
+
- [dark-mode-toggle.html](#12-dark-mode-togglehtml)
|
|
27
28
|
- [Dynamic section rendering (resume-section-*.html)](#dynamic-section-rendering-resume-section-html)
|
|
28
29
|
- [How it works](#how-it-works)
|
|
29
30
|
- [Section list and flags](#section-list-and-flags)
|
|
@@ -273,6 +274,36 @@ To add a new icon:
|
|
|
273
274
|
|
|
274
275
|
---
|
|
275
276
|
|
|
277
|
+
### 12) dark-mode-toggle.html
|
|
278
|
+
Purpose: Self-contained dark mode toggle button rendered conditionally in both resume layouts.
|
|
279
|
+
- Outputs a fixed-position circular button (top-right for LTR, top-left for RTL) with inline sun/moon SVG icons.
|
|
280
|
+
- Implements a **two-state** finite state machine:
|
|
281
|
+
- **System Default** — follows the OS `prefers-color-scheme` preference automatically. No `localStorage` entry.
|
|
282
|
+
- **Pinned** — forces light or dark regardless of OS. Saves `"light"` or `"dark"` to `localStorage["color-scheme"]`.
|
|
283
|
+
- Includes an `addEventListener("change")` listener on `matchMedia("(prefers-color-scheme: dark)")` so the button icon updates in real-time if the user changes their OS theme while in System Default mode.
|
|
284
|
+
- Fully accessible: keyboard focusable, dynamic `aria-label` and `title` updated on state changes, bilingual labels (English and Arabic).
|
|
285
|
+
- Hides automatically in print/PDF output via `.no-print`.
|
|
286
|
+
|
|
287
|
+
Configuration: Controlled by `resume_dark_mode` in `_config.yml`. Only rendered when set to `enabled`.
|
|
288
|
+
|
|
289
|
+
```yaml
|
|
290
|
+
# _config.yml
|
|
291
|
+
resume_dark_mode: enabled # renders toggle in both EN and AR layouts
|
|
292
|
+
# resume_dark_mode: auto # CSS-only system detection (default, no toggle)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
The include is conditionally pulled into both `resume-en.html` and `resume-ar.html`:
|
|
296
|
+
|
|
297
|
+
```liquid
|
|
298
|
+
{% if site.resume_dark_mode == "enabled" %}
|
|
299
|
+
{% include dark-mode-toggle.html %}
|
|
300
|
+
{% endif %}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Used by: `resume-en.html` and `resume-ar.html`.
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
276
307
|
## Dynamic section rendering (resume-section-*.html)
|
|
277
308
|
|
|
278
309
|
Two includes power all resume content:
|
data/docs/LAYOUTS_GUIDE.md
CHANGED
|
@@ -146,7 +146,7 @@ active_resume_path_ar: ""
|
|
|
146
146
|
## Rendering flow inside resume layouts
|
|
147
147
|
|
|
148
148
|
### 1) Head, SEO, analytics
|
|
149
|
-
- Shared meta from `shared-head.html`
|
|
149
|
+
- Shared meta from `shared-head.html` — includes `<meta name="color-scheme" content="light dark">` and an inline FOUC-prevention script that reads `localStorage["color-scheme"]` and applies the saved theme attribute before first paint
|
|
150
150
|
- Resume-specific head from `resume-head-*.html` (CSS, fonts, favicons)
|
|
151
151
|
- `{% seo %}` prints SEO meta and Open Graph/Twitter cards
|
|
152
152
|
- `analytics-head.html` adds GTM or GA4; `analytics-body.html` adds GTM `<noscript>` in the body
|
|
@@ -250,6 +250,11 @@ resume_section_order:
|
|
|
250
250
|
# Print behavior
|
|
251
251
|
resume_print_social_links: true
|
|
252
252
|
|
|
253
|
+
# Dark mode (controls whether a toggle button is rendered)
|
|
254
|
+
# "auto" (default): CSS-only system detection, no toggle button
|
|
255
|
+
# "enabled": renders an interactive toggle button in both EN and AR layouts
|
|
256
|
+
resume_dark_mode: auto
|
|
257
|
+
|
|
253
258
|
# Active data subtree for resume_data (separate for each language)
|
|
254
259
|
active_resume_path_en: "en"
|
|
255
260
|
active_resume_path_ar: "ar"
|
data/docs/SASS_GUIDE.md
CHANGED
|
@@ -22,6 +22,7 @@ A contributor- and user-friendly tour of the theme's styling system: how styles
|
|
|
22
22
|
- [_resume-rtl.scss](#7-_resume-rtlscss)
|
|
23
23
|
- [_profile.scss](#8-_profilescss)
|
|
24
24
|
- [_all-pages.scss](#9-_all-pagesscss)
|
|
25
|
+
- [_dark-mode.scss](#10-_dark-modescss)
|
|
25
26
|
- [Responsive, print, and RTL strategy](#responsive-print-and-rtl-strategy)
|
|
26
27
|
- [Variables reference](#variables-reference)
|
|
27
28
|
- [Mixins reference](#mixins-reference)
|
|
@@ -163,6 +164,38 @@ Purpose: Small shared bits across pages.
|
|
|
163
164
|
|
|
164
165
|
---
|
|
165
166
|
|
|
167
|
+
### 10) `_dark-mode.scss`
|
|
168
|
+
Purpose: Dark mode color token system and toggle button styles.
|
|
169
|
+
|
|
170
|
+
This partial is the single source of truth for all theming colors. It defines CSS custom properties on `:root` for the light palette and overrides them for dark mode via two mechanisms:
|
|
171
|
+
|
|
172
|
+
1. **Automatic** — `@media (prefers-color-scheme: dark)` picks up the user's OS preference with no JavaScript.
|
|
173
|
+
2. **Manual pin** — `[data-color-scheme="dark"]` / `[data-color-scheme="light"]` attributes on `<html>` are set by the `dark-mode-toggle.html` JS when the user pins a preference.
|
|
174
|
+
|
|
175
|
+
It also includes:
|
|
176
|
+
- **Print reset** — overrides all tokens back to black-on-white under `@media print`, ensuring PDFs are always clean regardless of the active theme.
|
|
177
|
+
- **Toggle button styles** — `.dark-mode-toggle` component: fixed-position, circular, with sun/moon icon visibility rules per theme state and RTL flip via `html[dir="rtl"]`.
|
|
178
|
+
|
|
179
|
+
Key CSS custom properties defined:
|
|
180
|
+
|
|
181
|
+
| Token | Purpose |
|
|
182
|
+
|---|---|
|
|
183
|
+
| `--bg-color` | Page/html background |
|
|
184
|
+
| `--text-color` | Primary body text |
|
|
185
|
+
| `--text-muted` | Secondary/muted text (e.g., footer) |
|
|
186
|
+
| `--border-color` | Section borders |
|
|
187
|
+
| `--card-bg` | Button and card backgrounds |
|
|
188
|
+
| `--link-color` | Link default color |
|
|
189
|
+
| `--link-hover` | Link hover color |
|
|
190
|
+
| `--accent-color` | Focus rings and UI accent |
|
|
191
|
+
| `--button-bg/text/hover-*` | Contact and CV button states |
|
|
192
|
+
| `--icon-fill` | SVG icon fill |
|
|
193
|
+
| `--selection-bg/color` | Text selection highlight |
|
|
194
|
+
|
|
195
|
+
Loaded by: `assets/css/cv.scss`, `assets/css/cv-ar.scss`, and `assets/css/main.scss`.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
166
199
|
## Responsive, print, and RTL strategy
|
|
167
200
|
|
|
168
201
|
- Responsive: Mixins `media_larger_than_mobile` and `media_mobile` (600px breakpoint) provide simple adjustments (e.g., stacking → floats).
|
|
@@ -131,6 +131,11 @@ resume_print_social_links: true # Include social links on printed version
|
|
|
131
131
|
authors: [] # optional
|
|
132
132
|
resume_theme: default # Theme variant (currently only "default")
|
|
133
133
|
|
|
134
|
+
# Dark mode configuration:
|
|
135
|
+
# - "auto" (default): Automatic dark mode based on user's OS/browser preference (no toggle button)
|
|
136
|
+
# - "enabled": Adds an interactive floating toggle button (English LTR and Arabic RTL supported)
|
|
137
|
+
# resume_dark_mode: auto
|
|
138
|
+
|
|
134
139
|
# Analytics (optional - choose one)
|
|
135
140
|
analytics:
|
|
136
141
|
# Google Tag Manager (recommended):
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bilingual-jekyll-resume-theme
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Khaldoon Mutahar
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-08-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jekyll
|
|
@@ -109,6 +109,7 @@ files:
|
|
|
109
109
|
- _includes/analytics-body.html
|
|
110
110
|
- _includes/analytics-head.html
|
|
111
111
|
- _includes/ar-date.html
|
|
112
|
+
- _includes/dark-mode-toggle.html
|
|
112
113
|
- _includes/hreflang.html
|
|
113
114
|
- _includes/main-head.html
|
|
114
115
|
- _includes/print-social-links.html
|
|
@@ -141,6 +142,7 @@ files:
|
|
|
141
142
|
- _layouts/resume-en.html
|
|
142
143
|
- _sass/_all-pages.scss
|
|
143
144
|
- _sass/_base.scss
|
|
145
|
+
- _sass/_dark-mode.scss
|
|
144
146
|
- _sass/_layout.scss
|
|
145
147
|
- _sass/_mixins.scss
|
|
146
148
|
- _sass/_normalize.scss
|