spectrum-er 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.htaccess +34 -0
- data/LICENSE.txt +21 -0
- data/README.md +330 -0
- data/_config.yml +340 -0
- data/_data/navigation.yml +38 -0
- data/_includes/content-card.html +30 -0
- data/_includes/gallery-track.html +97 -0
- data/_includes/photo-card.html +23 -0
- data/_includes/section-header.html +12 -0
- data/_includes/site-navigation.html +33 -0
- data/_includes/structured-data.html +22 -0
- data/_includes/tech-bite-card.html +52 -0
- data/_layouts/about.html +63 -0
- data/_layouts/default.html +210 -0
- data/_layouts/films-list.html +27 -0
- data/_layouts/tech-bite-list.html +105 -0
- data/_layouts/tech-bite.html +60 -0
- data/_sass/_base.scss +298 -0
- data/_sass/_color-variables.scss +262 -0
- data/_sass/_components.scss +519 -0
- data/_sass/_layouts.scss +820 -0
- data/_sass/_sections.scss +163 -0
- data/_sass/_utilities.scss +301 -0
- data/assets/css/main.scss +21 -0
- data/assets/images/films/IMG_2112.JPG +0 -0
- data/assets/images/films/IMG_8987.jpg +0 -0
- data/assets/images/films/IMG_8998.jpg +0 -0
- data/assets/images/films/IMG_9270.jpg +0 -0
- data/assets/images/films/IMG_9504.jpg +0 -0
- data/assets/images/films/IMG_9570.jpg +0 -0
- data/assets/js/theme-manager.js +271 -0
- metadata +150 -0
data/_config.yml
ADDED
@@ -0,0 +1,340 @@
|
|
1
|
+
# ===========================================
|
2
|
+
# SPECTRUM JEKYLL THEME - MAIN CONFIGURATION
|
3
|
+
# ===========================================
|
4
|
+
|
5
|
+
# Jekyll Core Settings
|
6
|
+
markdown: kramdown
|
7
|
+
highlighter: rouge
|
8
|
+
permalink: pretty
|
9
|
+
timezone: UTC
|
10
|
+
|
11
|
+
# Site Identity
|
12
|
+
site:
|
13
|
+
title: "Duhyeon Kim"
|
14
|
+
description: "Personal website showcasing tech insights and development journey"
|
15
|
+
url: "https://yoursite.com"
|
16
|
+
baseurl: ""
|
17
|
+
author: "Duhyeon Kim"
|
18
|
+
email: "your@email.com"
|
19
|
+
|
20
|
+
# Personal Information
|
21
|
+
personal:
|
22
|
+
name: "Duhyeon Kim"
|
23
|
+
tagline: "spectrum jekyll owner"
|
24
|
+
bio: "Developer passionate about technology, design, and continuous learning"
|
25
|
+
location: "Seoul, Korea"
|
26
|
+
|
27
|
+
# Social Media Links
|
28
|
+
social:
|
29
|
+
enabled: true
|
30
|
+
platforms:
|
31
|
+
email:
|
32
|
+
enabled: true
|
33
|
+
url: "kdhluck@naver.com"
|
34
|
+
label: "Email"
|
35
|
+
icon: "fa-solid fa-envelope"
|
36
|
+
github:
|
37
|
+
enabled: true
|
38
|
+
url: "https://github.com/dudududukim"
|
39
|
+
label: "GitHub"
|
40
|
+
icon: "fa-brands fa-github"
|
41
|
+
linkedin:
|
42
|
+
enabled: true
|
43
|
+
url: "https://www.linkedin.com/in/duhyeon-kim-6623082b1/"
|
44
|
+
label: "LinkedIn"
|
45
|
+
icon: "fa-brands fa-linkedin"
|
46
|
+
twitter:
|
47
|
+
enabled: false
|
48
|
+
url: "https://twitter.com/username"
|
49
|
+
label: "Twitter"
|
50
|
+
icon: "fa-brands fa-twitter"
|
51
|
+
instagram:
|
52
|
+
enabled: false
|
53
|
+
url: "https://instagram.com/username"
|
54
|
+
label: "Instagram"
|
55
|
+
icon: "fa-brands fa-instagram"
|
56
|
+
|
57
|
+
# Accessibility Configuration
|
58
|
+
accessibility:
|
59
|
+
skip_links: true
|
60
|
+
focus_indicators: true
|
61
|
+
screen_reader_text: "Skip to main content"
|
62
|
+
high_contrast_mode: false
|
63
|
+
reduced_motion: false
|
64
|
+
|
65
|
+
# Navigation Configuration
|
66
|
+
navigation:
|
67
|
+
home_text: "Home"
|
68
|
+
show_social: true
|
69
|
+
social_position: "right"
|
70
|
+
|
71
|
+
# Footer Configuration
|
72
|
+
footer:
|
73
|
+
copyright_text: "All rights reserved."
|
74
|
+
show_theme_credit: true
|
75
|
+
theme_name: "Spectrum"
|
76
|
+
theme_url: "https://github.com/dudududukim/spectrum"
|
77
|
+
|
78
|
+
# Content Configuration
|
79
|
+
content:
|
80
|
+
pages:
|
81
|
+
about:
|
82
|
+
title: "About"
|
83
|
+
welcome_message: "Welcome 🤗"
|
84
|
+
intro_text: "I'm Duhyeon Kim, an Electrical and Electronic Engineering major from Korea University. I have a broad curiosity about all things AI, and I enjoy diving deep into areas where I can focus and grow."
|
85
|
+
main_description: "My hope is that, even if just a little, the path I take will leave something valuable for humanity. My dreams are big, but I'm still figuring out how to make them a reality."
|
86
|
+
sections:
|
87
|
+
- title: "Areas of Study & Work"
|
88
|
+
content: "Here are a few topics I've studied or worked on:\n\n- Computer vision / NLP\n- FPGA & VLSI\n- Computer architecture"
|
89
|
+
closing_text: "🌈 Wishing everyone a wonderful day!"
|
90
|
+
|
91
|
+
tech_bites:
|
92
|
+
title: "Tech Bites"
|
93
|
+
description: "Daily tech insights and discoveries"
|
94
|
+
list_page:
|
95
|
+
welcome_message: "Welcome to my collection of tech insights!"
|
96
|
+
intro_text: "Here you'll find bite-sized posts covering everything from JavaScript tips to performance optimization techniques, accessibility best practices, and modern web development workflows."
|
97
|
+
description: "Each post is designed to be a quick read while providing practical, actionable information that you can apply to your own projects. Whether you're a seasoned developer or just starting out, there's something here for everyone."
|
98
|
+
categories_title: "Categories"
|
99
|
+
categories:
|
100
|
+
- name: "JavaScript"
|
101
|
+
description: "Modern ES6+ features, frameworks, and best practices"
|
102
|
+
- name: "CSS"
|
103
|
+
description: "Layout techniques, animations, and responsive design"
|
104
|
+
- name: "Performance"
|
105
|
+
description: "Optimization strategies and Core Web Vitals"
|
106
|
+
- name: "Accessibility"
|
107
|
+
description: "Building inclusive web experiences"
|
108
|
+
- name: "Development"
|
109
|
+
description: "Workflows, tools, and productivity tips"
|
110
|
+
closing_text: "Browse through the posts below, or use the category filter to find content that interests you most."
|
111
|
+
card:
|
112
|
+
read_more_text: "Read More"
|
113
|
+
min_read_text: "min read"
|
114
|
+
navigation:
|
115
|
+
previous_text: "← Previous"
|
116
|
+
next_text: "Next →"
|
117
|
+
|
118
|
+
films:
|
119
|
+
title: "Films"
|
120
|
+
description: "Photography collection showcasing moments captured through the lens"
|
121
|
+
|
122
|
+
ui:
|
123
|
+
buttons:
|
124
|
+
view_all_tech_bites: "View All Tech Bites"
|
125
|
+
read_more: "Read More"
|
126
|
+
|
127
|
+
pagination:
|
128
|
+
showing_text: "Showing"
|
129
|
+
of_text: "of"
|
130
|
+
tech_bites_text: "tech bites"
|
131
|
+
page_text: "Page"
|
132
|
+
previous_text: "Previous"
|
133
|
+
next_text: "Next"
|
134
|
+
|
135
|
+
error:
|
136
|
+
404:
|
137
|
+
title: "Page Not Found"
|
138
|
+
message: "Sorry, the page you're looking for doesn't exist."
|
139
|
+
suggestion: "You might want to go back to the homepage or check out our tech bites."
|
140
|
+
back_home_text: "Back to Home"
|
141
|
+
|
142
|
+
# Theme Configuration (consolidated from theme.yml)
|
143
|
+
site_theme:
|
144
|
+
colors:
|
145
|
+
primary: "#3498db"
|
146
|
+
secondary: "#2c3e50"
|
147
|
+
accent: "#e74c3c"
|
148
|
+
background: "#ffffff"
|
149
|
+
text: "#2c3e50"
|
150
|
+
light_text: "#7f8c8d"
|
151
|
+
success: "#27ae60"
|
152
|
+
warning: "#f39c12"
|
153
|
+
error: "#e74c3c"
|
154
|
+
info: "#3498db"
|
155
|
+
|
156
|
+
typography:
|
157
|
+
font_family: "Lato"
|
158
|
+
font_weights:
|
159
|
+
light: 300
|
160
|
+
normal: 400
|
161
|
+
medium: 500
|
162
|
+
bold: 700
|
163
|
+
font_sizes:
|
164
|
+
xs: "0.75rem"
|
165
|
+
sm: "0.875rem"
|
166
|
+
base: "1rem"
|
167
|
+
lg: "1.125rem"
|
168
|
+
xl: "1.25rem"
|
169
|
+
"2xl": "1.5rem"
|
170
|
+
"3xl": "1.875rem"
|
171
|
+
"4xl": "2.25rem"
|
172
|
+
|
173
|
+
layout:
|
174
|
+
max_width: "1125px"
|
175
|
+
content_width: "875px"
|
176
|
+
breakpoints:
|
177
|
+
mobile: "768px"
|
178
|
+
tablet: "1024px"
|
179
|
+
desktop: "1200px"
|
180
|
+
|
181
|
+
animations:
|
182
|
+
enabled: true
|
183
|
+
duration: "0.3s"
|
184
|
+
easing: "ease"
|
185
|
+
reduced_motion: false
|
186
|
+
|
187
|
+
dark_mode:
|
188
|
+
enabled: true
|
189
|
+
auto_detect: true
|
190
|
+
toggle_enabled: true
|
191
|
+
|
192
|
+
components:
|
193
|
+
cards:
|
194
|
+
border_radius: "0.75rem"
|
195
|
+
shadow: "0 4px 6px rgba(0, 0, 0, 0.1)"
|
196
|
+
hover_shadow: "0 8px 25px rgba(0, 0, 0, 0.15)"
|
197
|
+
buttons:
|
198
|
+
border_radius: "0.5rem"
|
199
|
+
padding: "0.75rem 1.5rem"
|
200
|
+
transition: "all 0.3s ease"
|
201
|
+
navigation:
|
202
|
+
height: "4rem"
|
203
|
+
background: "var(--bg-color)"
|
204
|
+
border_bottom: "1px solid var(--border-light)"
|
205
|
+
footer:
|
206
|
+
padding: "0.75rem 0"
|
207
|
+
background: "var(--bg-color)"
|
208
|
+
border_top: "1px solid var(--border-light)"
|
209
|
+
|
210
|
+
accessibility:
|
211
|
+
focus_ring: "2px solid var(--primary-color)"
|
212
|
+
focus_offset: "2px"
|
213
|
+
high_contrast: false
|
214
|
+
reduced_motion: false
|
215
|
+
skip_links: true
|
216
|
+
|
217
|
+
# Homepage Section Controls
|
218
|
+
homepage_sections:
|
219
|
+
tech_bites_preview:
|
220
|
+
enabled: true
|
221
|
+
show_count: 5
|
222
|
+
show_read_more_button: true
|
223
|
+
custom_title: "Recent Tech Bites" # Optional override
|
224
|
+
|
225
|
+
films_preview:
|
226
|
+
enabled: true
|
227
|
+
show_count: 6
|
228
|
+
show_view_all_button: true
|
229
|
+
custom_title: "Recent Films" # Optional override
|
230
|
+
animation_duration: 60
|
231
|
+
|
232
|
+
# Future extensibility
|
233
|
+
papers_preview:
|
234
|
+
enabled: false
|
235
|
+
show_count: 3
|
236
|
+
|
237
|
+
gallery_preview:
|
238
|
+
enabled: false
|
239
|
+
show_count: 8
|
240
|
+
|
241
|
+
# Design Customization (maintain current defaults)
|
242
|
+
design_options:
|
243
|
+
tech_bite_cards:
|
244
|
+
compact_mode: true # Current ultra-compact design
|
245
|
+
show_excerpts: true
|
246
|
+
show_reading_time: true
|
247
|
+
max_excerpt_length: 150
|
248
|
+
|
249
|
+
gallery_track:
|
250
|
+
auto_scroll: true
|
251
|
+
scroll_speed: 60 # Current animation duration
|
252
|
+
show_overlay_on_hover: true
|
253
|
+
|
254
|
+
navigation:
|
255
|
+
fixed_position: true
|
256
|
+
show_breadcrumbs: true
|
257
|
+
social_position: "right" # Current position
|
258
|
+
|
259
|
+
footer:
|
260
|
+
show_theme_credit: true # Current setting
|
261
|
+
compact_layout: true # Current design
|
262
|
+
|
263
|
+
# Section Configuration
|
264
|
+
sections:
|
265
|
+
tech_bite:
|
266
|
+
enabled: true
|
267
|
+
main_page_count: 5
|
268
|
+
show_categories: true
|
269
|
+
show_dates: true
|
270
|
+
pagination: 10
|
271
|
+
|
272
|
+
gallery_track:
|
273
|
+
enabled: true
|
274
|
+
image_count: 6
|
275
|
+
animation_duration: 60
|
276
|
+
|
277
|
+
# Collections
|
278
|
+
collections:
|
279
|
+
tech-bites:
|
280
|
+
output: true
|
281
|
+
permalink: /tech-bites/:name/
|
282
|
+
papers:
|
283
|
+
output: true
|
284
|
+
permalink: /papers/:name/
|
285
|
+
gallery:
|
286
|
+
output: true
|
287
|
+
permalink: /gallery/:name/
|
288
|
+
|
289
|
+
# Default Front Matter
|
290
|
+
defaults:
|
291
|
+
- scope:
|
292
|
+
path: ""
|
293
|
+
type: "pages"
|
294
|
+
values:
|
295
|
+
layout: "default"
|
296
|
+
- scope:
|
297
|
+
path: ""
|
298
|
+
type: "tech-bites"
|
299
|
+
values:
|
300
|
+
layout: "tech-bite"
|
301
|
+
show_sidebar: true
|
302
|
+
|
303
|
+
# Plugins
|
304
|
+
plugins:
|
305
|
+
- jekyll-feed
|
306
|
+
- jekyll-sitemap
|
307
|
+
- jekyll-seo-tag
|
308
|
+
|
309
|
+
# Build Settings
|
310
|
+
exclude:
|
311
|
+
- Gemfile
|
312
|
+
- Gemfile.lock
|
313
|
+
- node_modules
|
314
|
+
- vendor/bundle/
|
315
|
+
- vendor/cache/
|
316
|
+
- vendor/gems/
|
317
|
+
- vendor/ruby/
|
318
|
+
- README.md
|
319
|
+
- .gitignore
|
320
|
+
- .sass-cache/
|
321
|
+
- .jekyll-cache/
|
322
|
+
- .jekyll-metadata
|
323
|
+
|
324
|
+
# Sass Configuration
|
325
|
+
sass:
|
326
|
+
style: compressed
|
327
|
+
sass_dir: _sass
|
328
|
+
load_paths:
|
329
|
+
- assets/css
|
330
|
+
|
331
|
+
# Kramdown Configuration
|
332
|
+
kramdown:
|
333
|
+
input: GFM
|
334
|
+
syntax_highlighter: rouge
|
335
|
+
syntax_highlighter_opts:
|
336
|
+
css_class: 'highlight'
|
337
|
+
span:
|
338
|
+
line_numbers: false
|
339
|
+
block:
|
340
|
+
line_numbers: false
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Navigation Configuration
|
2
|
+
# =======================
|
3
|
+
|
4
|
+
primary:
|
5
|
+
- title: "Home"
|
6
|
+
url: "/"
|
7
|
+
icon: "fa-solid fa-home"
|
8
|
+
- title: "Tech Bites"
|
9
|
+
url: "/tech-bites/"
|
10
|
+
icon: "fa-solid fa-code"
|
11
|
+
- title: "Films"
|
12
|
+
url: "/films/"
|
13
|
+
icon: "fa-solid fa-camera"
|
14
|
+
- title: "Papers"
|
15
|
+
url: "/papers/"
|
16
|
+
icon: "fa-solid fa-file-text"
|
17
|
+
enabled: false # Disabled until implemented
|
18
|
+
- title: "About"
|
19
|
+
url: "/about/"
|
20
|
+
icon: "fa-solid fa-user"
|
21
|
+
- title: "Gallery"
|
22
|
+
url: "/gallery/"
|
23
|
+
icon: "fa-solid fa-images"
|
24
|
+
enabled: false # Disabled until implemented
|
25
|
+
|
26
|
+
# Footer navigation
|
27
|
+
footer:
|
28
|
+
- title: "Privacy Policy"
|
29
|
+
url: "/privacy/"
|
30
|
+
enabled: false
|
31
|
+
- title: "Terms of Service"
|
32
|
+
url: "/terms/"
|
33
|
+
enabled: false
|
34
|
+
- title: "Contact"
|
35
|
+
url: "/contact/"
|
36
|
+
enabled: false
|
37
|
+
|
38
|
+
# Social navigation references main config social settings
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<!-- Content Card Component -->
|
2
|
+
<!-- Usage: {% include content-card.html title="Card Title" meta="Meta info" content="Card content" %} -->
|
3
|
+
|
4
|
+
<article class="content-card card">
|
5
|
+
{% if include.title %}
|
6
|
+
<div class="card-header">
|
7
|
+
<h3 class="card-title">
|
8
|
+
{% if include.url %}
|
9
|
+
<a href="{{ include.url }}">{{ include.title }}</a>
|
10
|
+
{% else %}
|
11
|
+
{{ include.title }}
|
12
|
+
{% endif %}
|
13
|
+
</h3>
|
14
|
+
</div>
|
15
|
+
{% endif %}
|
16
|
+
|
17
|
+
{% if include.meta %}
|
18
|
+
<div class="card-meta">{{ include.meta }}</div>
|
19
|
+
{% endif %}
|
20
|
+
|
21
|
+
{% if include.content %}
|
22
|
+
<div class="card-body">
|
23
|
+
<div class="card-content">{{ include.content }}</div>
|
24
|
+
</div>
|
25
|
+
{% endif %}
|
26
|
+
|
27
|
+
{% if include.footer %}
|
28
|
+
<div class="card-footer">{{ include.footer }}</div>
|
29
|
+
{% endif %}
|
30
|
+
</article>
|
@@ -0,0 +1,97 @@
|
|
1
|
+
<!-- Gallery Track Component -->
|
2
|
+
<!-- Horizontal scrolling gallery showing recent films -->
|
3
|
+
{% assign animation_duration = site.sections.gallery_track.animation_duration | default: 45 %}
|
4
|
+
|
5
|
+
<style>
|
6
|
+
.gallery-track {
|
7
|
+
animation: scroll-left {{ animation_duration }}s linear infinite !important;
|
8
|
+
}
|
9
|
+
</style>
|
10
|
+
|
11
|
+
<section class="gallery-track-preview">
|
12
|
+
<div class="section-header">
|
13
|
+
<h2 class="section-title">Recent Films</h2>
|
14
|
+
<p class="section-description">Latest photography from my collection</p>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<div class="section-content">
|
18
|
+
<div class="gallery-track-container">
|
19
|
+
<div class="gallery-track" id="gallery-track">
|
20
|
+
{% assign image_extensions = "jpg,jpeg,png,webp,JPG,JPEG,PNG,WEBP" | split: "," %}
|
21
|
+
{% assign films_images = site.static_files | where_exp: "file", "file.path contains '/assets/images/films/'" %}
|
22
|
+
{% assign image_count = site.sections.gallery_track.image_count | default: 6 %}
|
23
|
+
{% assign recent_films = films_images | limit: image_count %}
|
24
|
+
|
25
|
+
<!-- Generate gallery items with seamless loop duplication -->
|
26
|
+
{% for i in (1..2) %}
|
27
|
+
{% for file in recent_films %}
|
28
|
+
{% assign file_ext = file.extname | remove: '.' %}
|
29
|
+
{% if image_extensions contains file_ext %}
|
30
|
+
{% assign image_name = file.name | remove: file.extname %}
|
31
|
+
{% assign image_path = file.path | relative_url %}
|
32
|
+
|
33
|
+
<div class="gallery-item">
|
34
|
+
<a href="{{ '/films/' | relative_url }}" class="gallery-link" aria-label="View {{ image_name | replace: '-', ' ' | capitalize }} in gallery">
|
35
|
+
<img
|
36
|
+
src="{{ image_path }}"
|
37
|
+
alt="{{ image_name | replace: '-', ' ' | capitalize }}"
|
38
|
+
class="gallery-image"
|
39
|
+
loading="lazy"
|
40
|
+
decoding="async"
|
41
|
+
/>
|
42
|
+
<div class="gallery-overlay">
|
43
|
+
<div class="gallery-info">
|
44
|
+
<span class="gallery-title">{{ image_name | replace: '-', ' ' | capitalize }}</span>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
</a>
|
48
|
+
</div>
|
49
|
+
{% endif %}
|
50
|
+
{% endfor %}
|
51
|
+
{% endfor %}
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
|
55
|
+
<div class="section-footer">
|
56
|
+
<a href="{{ '/films/' | relative_url }}" class="section-button">
|
57
|
+
View All Films
|
58
|
+
<span class="btn-icon">→</span>
|
59
|
+
</a>
|
60
|
+
</div>
|
61
|
+
</div>
|
62
|
+
</section>
|
63
|
+
|
64
|
+
<script>
|
65
|
+
document.addEventListener('DOMContentLoaded', function() {
|
66
|
+
const galleryTrack = document.getElementById('gallery-track');
|
67
|
+
if (!galleryTrack) return;
|
68
|
+
|
69
|
+
let isPaused = false;
|
70
|
+
|
71
|
+
// Pause animation on hover
|
72
|
+
galleryTrack.addEventListener('mouseenter', function() {
|
73
|
+
isPaused = true;
|
74
|
+
galleryTrack.style.animationPlayState = 'paused';
|
75
|
+
});
|
76
|
+
|
77
|
+
// Resume animation on mouse leave
|
78
|
+
galleryTrack.addEventListener('mouseleave', function() {
|
79
|
+
isPaused = false;
|
80
|
+
galleryTrack.style.animationPlayState = 'running';
|
81
|
+
});
|
82
|
+
|
83
|
+
// Pause animation when any gallery item is hovered
|
84
|
+
const galleryItems = galleryTrack.querySelectorAll('.gallery-item');
|
85
|
+
galleryItems.forEach(item => {
|
86
|
+
item.addEventListener('mouseenter', function() {
|
87
|
+
isPaused = true;
|
88
|
+
galleryTrack.style.animationPlayState = 'paused';
|
89
|
+
});
|
90
|
+
|
91
|
+
item.addEventListener('mouseleave', function() {
|
92
|
+
isPaused = false;
|
93
|
+
galleryTrack.style.animationPlayState = 'running';
|
94
|
+
});
|
95
|
+
});
|
96
|
+
});
|
97
|
+
</script>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
{% assign image = include.image %}
|
2
|
+
{% assign image_name = image.name | remove: image.extname %}
|
3
|
+
{% assign image_path = image.path | relative_url %}
|
4
|
+
|
5
|
+
<article class="photo-card card" data-image="{{ image_name }}">
|
6
|
+
<div class="photo-container">
|
7
|
+
<a href="{{ image_path }}" class="photo-link" data-lightbox="films" data-title="{{ image_name | replace: '-', ' ' | capitalize }}">
|
8
|
+
<img
|
9
|
+
src="{{ image_path }}"
|
10
|
+
alt="{{ image_name | replace: '-', ' ' | capitalize }}"
|
11
|
+
class="photo-image"
|
12
|
+
loading="lazy"
|
13
|
+
decoding="async"
|
14
|
+
/>
|
15
|
+
<div class="photo-overlay">
|
16
|
+
<div class="photo-info">
|
17
|
+
<h3 class="photo-title">{{ image_name | replace: '-', ' ' | capitalize }}</h3>
|
18
|
+
<span class="photo-meta">{{ image.extname | remove: '.' | upcase }}</span>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</a>
|
22
|
+
</div>
|
23
|
+
</article>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<!-- Section Header Component -->
|
2
|
+
<!-- Usage: {% include section-header.html title="Section Title" description="Section description" %} -->
|
3
|
+
|
4
|
+
<div class="section-header">
|
5
|
+
{% if include.title %}
|
6
|
+
<h2 class="section-title">{{ include.title }}</h2>
|
7
|
+
{% endif %}
|
8
|
+
|
9
|
+
{% if include.description %}
|
10
|
+
<p class="section-description">{{ include.description }}</p>
|
11
|
+
{% endif %}
|
12
|
+
</div>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<nav class="site-navigation" role="navigation" aria-label="Site navigation">
|
2
|
+
<div class="nav-container">
|
3
|
+
<div class="nav-path">
|
4
|
+
<a href="{{ '/' | relative_url }}" class="nav-home">{{ site.navigation.home_text | default: "Home" }}</a>
|
5
|
+
{% unless page.url == "/" %}
|
6
|
+
<span class="nav-separator">/</span>
|
7
|
+
{% if page.layout == 'tech-bite-list' %}
|
8
|
+
<span class="nav-current">{{ site.content.pages.tech_bites.title | default: "Tech Bites" }}</span>
|
9
|
+
{% elsif page.layout == 'tech-bite' %}
|
10
|
+
<a href="{{ '/tech-bites/' | relative_url }}" class="nav-link">{{ site.content.pages.tech_bites.title | default: "Tech Bites" }}</a>
|
11
|
+
<span class="nav-separator">/</span>
|
12
|
+
<span class="nav-current">{{ page.title }}</span>
|
13
|
+
{% elsif page.layout == 'about' %}
|
14
|
+
<span class="nav-current">{{ site.content.pages.about.title | default: "About" }}</span>
|
15
|
+
{% else %}
|
16
|
+
<span class="nav-current">{{ page.title | default: 'Page' }}</span>
|
17
|
+
{% endif %}
|
18
|
+
{% endunless %}
|
19
|
+
</div>
|
20
|
+
|
21
|
+
{% if site.navigation.show_social and site.social.enabled %}
|
22
|
+
<div class="nav-social">
|
23
|
+
{% for platform in site.social.platforms %}
|
24
|
+
{% if platform[1].enabled and platform[1].url %}
|
25
|
+
<a href="{{ platform[1].url }}" target="_blank" rel="noopener noreferrer" class="nav-social-link" title="{{ platform[1].label }}">
|
26
|
+
<i class="{{ platform[1].icon }}"></i>
|
27
|
+
</a>
|
28
|
+
{% endif %}
|
29
|
+
{% endfor %}
|
30
|
+
</div>
|
31
|
+
{% endif %}
|
32
|
+
</div>
|
33
|
+
</nav>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<!-- Structured Data for SEO -->
|
2
|
+
<script type="application/ld+json">
|
3
|
+
{
|
4
|
+
"@context": "https://schema.org",
|
5
|
+
"@type": "Person",
|
6
|
+
"name": "{{ site.personal.name | default: site.title }}",
|
7
|
+
"url": "{{ site.url }}{{ site.baseurl }}",
|
8
|
+
"description": "{{ site.description }}",
|
9
|
+
{% if site.personal.social_links %}
|
10
|
+
"sameAs": [
|
11
|
+
{% if site.personal.social_links.github %}"https://github.com/{{ site.personal.social_links.github }}"{% unless forloop.last %},{% endunless %}{% endif %}
|
12
|
+
{% if site.personal.social_links.linkedin %}"https://linkedin.com/in/{{ site.personal.social_links.linkedin }}"{% unless forloop.last %},{% endunless %}{% endif %}
|
13
|
+
{% if site.personal.social_links.twitter %}"https://twitter.com/{{ site.personal.social_links.twitter }}"{% unless forloop.last %},{% endunless %}{% endif %}
|
14
|
+
],
|
15
|
+
{% endif %}
|
16
|
+
"jobTitle": "{{ site.personal.title | default: 'Developer' }}",
|
17
|
+
"worksFor": {
|
18
|
+
"@type": "Organization",
|
19
|
+
"name": "{{ site.personal.company | default: 'Freelance' }}"
|
20
|
+
}
|
21
|
+
}
|
22
|
+
</script>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
{% assign tech_bite = include.tech_bite %}
|
2
|
+
<article class="tech-bite-card"
|
3
|
+
data-date="{{ tech_bite.date | date: '%Y-%m-%d' }}">
|
4
|
+
<div class="card-content">
|
5
|
+
<header class="card-header">
|
6
|
+
<h3 class="card-title">
|
7
|
+
<a href="{{ tech_bite.url | relative_url }}" class="card-link">
|
8
|
+
{{ tech_bite.title }}
|
9
|
+
</a>
|
10
|
+
</h3>
|
11
|
+
|
12
|
+
<div class="card-meta">
|
13
|
+
{% if site.sections.tech_bite.show_dates and tech_bite.date %}
|
14
|
+
<time class="card-date" datetime="{{ tech_bite.date | date_to_xmlschema }}">
|
15
|
+
{{ tech_bite.date | date: "%b %d, %Y" }}
|
16
|
+
</time>
|
17
|
+
{% endif %}
|
18
|
+
|
19
|
+
{% if site.sections.tech_bite.show_categories and tech_bite.categories %}
|
20
|
+
<div class="card-categories">
|
21
|
+
{% for category in tech_bite.categories limit: 2 %}
|
22
|
+
<span class="category-tag">{{ category }}</span>
|
23
|
+
{% endfor %}
|
24
|
+
{% if tech_bite.categories.size > 2 %}
|
25
|
+
<span class="category-more">+{{ tech_bite.categories.size | minus: 2 }}</span>
|
26
|
+
{% endif %}
|
27
|
+
</div>
|
28
|
+
{% endif %}
|
29
|
+
</div>
|
30
|
+
</header>
|
31
|
+
|
32
|
+
<div class="card-body">
|
33
|
+
{% if tech_bite.excerpt %}
|
34
|
+
<p class="card-excerpt">{{ tech_bite.excerpt | strip_html | truncate: 150 }}</p>
|
35
|
+
{% elsif tech_bite.description %}
|
36
|
+
<p class="card-excerpt">{{ tech_bite.description | strip_html | truncate: 150 }}</p>
|
37
|
+
{% endif %}
|
38
|
+
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<footer class="card-footer">
|
42
|
+
<a href="{{ tech_bite.url | relative_url }}" class="read-more">
|
43
|
+
{{ site.content.pages.tech_bites.card.read_more_text | default: "Read More" }}
|
44
|
+
<span class="read-more-icon">→</span>
|
45
|
+
</a>
|
46
|
+
|
47
|
+
{% if tech_bite.reading_time %}
|
48
|
+
<span class="reading-time">{{ tech_bite.reading_time }} {{ site.content.pages.tech_bites.card.min_read_text | default: "min read" }}</span>
|
49
|
+
{% endif %}
|
50
|
+
</footer>
|
51
|
+
</div>
|
52
|
+
</article>
|