devfolio-page 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +219 -0
- package/dist/cli/commands/init.js +282 -0
- package/dist/cli/commands/render.js +105 -0
- package/dist/cli/commands/themes.js +40 -0
- package/dist/cli/commands/validate.js +86 -0
- package/dist/cli/helpers/validate.js +99 -0
- package/dist/cli/index.js +51 -0
- package/dist/cli/postinstall.js +20 -0
- package/dist/cli/schemas/portfolio.schema.js +299 -0
- package/dist/generator/builder.js +551 -0
- package/dist/generator/markdown.js +57 -0
- package/dist/generator/themes/dark-academia/partials/education.html +15 -0
- package/dist/generator/themes/dark-academia/partials/experience.html +23 -0
- package/dist/generator/themes/dark-academia/partials/hero.html +15 -0
- package/dist/generator/themes/dark-academia/partials/projects.html +17 -0
- package/dist/generator/themes/dark-academia/partials/skills.html +11 -0
- package/dist/generator/themes/dark-academia/partials/writing.html +15 -0
- package/dist/generator/themes/dark-academia/script.js +91 -0
- package/dist/generator/themes/dark-academia/styles.css +913 -0
- package/dist/generator/themes/dark-academia/template.html +46 -0
- package/dist/generator/themes/dark-academia/templates/experiments-index.html +80 -0
- package/dist/generator/themes/dark-academia/templates/homepage.html +125 -0
- package/dist/generator/themes/dark-academia/templates/project.html +101 -0
- package/dist/generator/themes/dark-academia/templates/projects-index.html +80 -0
- package/dist/generator/themes/dark-academia/templates/writing-index.html +75 -0
- package/dist/generator/themes/modern/partials/education.html +14 -0
- package/dist/generator/themes/modern/partials/experience.html +21 -0
- package/dist/generator/themes/modern/partials/hero.html +15 -0
- package/dist/generator/themes/modern/partials/projects.html +17 -0
- package/dist/generator/themes/modern/partials/skills.html +11 -0
- package/dist/generator/themes/modern/partials/writing.html +14 -0
- package/dist/generator/themes/modern/script.js +136 -0
- package/dist/generator/themes/modern/styles.css +835 -0
- package/dist/generator/themes/modern/template.html +59 -0
- package/dist/generator/themes/modern/templates/experiments-index.html +78 -0
- package/dist/generator/themes/modern/templates/homepage.html +125 -0
- package/dist/generator/themes/modern/templates/project.html +98 -0
- package/dist/generator/themes/modern/templates/projects-index.html +79 -0
- package/dist/generator/themes/modern/templates/writing-index.html +73 -0
- package/dist/generator/themes/srcl/partials/education.html +27 -0
- package/dist/generator/themes/srcl/partials/experience.html +25 -0
- package/dist/generator/themes/srcl/partials/hero.html +22 -0
- package/dist/generator/themes/srcl/partials/projects.html +24 -0
- package/dist/generator/themes/srcl/partials/sections/code.html +8 -0
- package/dist/generator/themes/srcl/partials/sections/demo.html +8 -0
- package/dist/generator/themes/srcl/partials/sections/gallery.html +12 -0
- package/dist/generator/themes/srcl/partials/sections/image.html +6 -0
- package/dist/generator/themes/srcl/partials/sections/interactive.html +8 -0
- package/dist/generator/themes/srcl/partials/sections/metrics.html +10 -0
- package/dist/generator/themes/srcl/partials/sections/outcomes.html +5 -0
- package/dist/generator/themes/srcl/partials/sections/overview.html +5 -0
- package/dist/generator/themes/srcl/partials/sections/process.html +5 -0
- package/dist/generator/themes/srcl/partials/skills.html +21 -0
- package/dist/generator/themes/srcl/partials/writing.html +14 -0
- package/dist/generator/themes/srcl/script.js +354 -0
- package/dist/generator/themes/srcl/styles.css +1260 -0
- package/dist/generator/themes/srcl/template.html +46 -0
- package/dist/generator/themes/srcl/templates/experiments-index.html +66 -0
- package/dist/generator/themes/srcl/templates/homepage.html +136 -0
- package/dist/generator/themes/srcl/templates/project.html +96 -0
- package/dist/generator/themes/srcl/templates/projects-index.html +70 -0
- package/dist/generator/themes/srcl/templates/writing-index.html +61 -0
- package/dist/types/portfolio.js +4 -0
- package/package.json +58 -0
- package/src/generator/themes/dark-academia/partials/education.html +15 -0
- package/src/generator/themes/dark-academia/partials/experience.html +23 -0
- package/src/generator/themes/dark-academia/partials/hero.html +15 -0
- package/src/generator/themes/dark-academia/partials/projects.html +17 -0
- package/src/generator/themes/dark-academia/partials/skills.html +11 -0
- package/src/generator/themes/dark-academia/partials/writing.html +15 -0
- package/src/generator/themes/dark-academia/script.js +91 -0
- package/src/generator/themes/dark-academia/styles.css +913 -0
- package/src/generator/themes/dark-academia/template.html +46 -0
- package/src/generator/themes/dark-academia/templates/experiments-index.html +80 -0
- package/src/generator/themes/dark-academia/templates/homepage.html +125 -0
- package/src/generator/themes/dark-academia/templates/project.html +101 -0
- package/src/generator/themes/dark-academia/templates/projects-index.html +80 -0
- package/src/generator/themes/dark-academia/templates/writing-index.html +75 -0
- package/src/generator/themes/modern/partials/education.html +14 -0
- package/src/generator/themes/modern/partials/experience.html +21 -0
- package/src/generator/themes/modern/partials/hero.html +15 -0
- package/src/generator/themes/modern/partials/projects.html +17 -0
- package/src/generator/themes/modern/partials/skills.html +11 -0
- package/src/generator/themes/modern/partials/writing.html +14 -0
- package/src/generator/themes/modern/script.js +136 -0
- package/src/generator/themes/modern/styles.css +835 -0
- package/src/generator/themes/modern/template.html +59 -0
- package/src/generator/themes/modern/templates/experiments-index.html +78 -0
- package/src/generator/themes/modern/templates/homepage.html +125 -0
- package/src/generator/themes/modern/templates/project.html +98 -0
- package/src/generator/themes/modern/templates/projects-index.html +79 -0
- package/src/generator/themes/modern/templates/writing-index.html +73 -0
- package/src/generator/themes/srcl/partials/education.html +27 -0
- package/src/generator/themes/srcl/partials/experience.html +25 -0
- package/src/generator/themes/srcl/partials/hero.html +22 -0
- package/src/generator/themes/srcl/partials/projects.html +24 -0
- package/src/generator/themes/srcl/partials/sections/code.html +8 -0
- package/src/generator/themes/srcl/partials/sections/demo.html +8 -0
- package/src/generator/themes/srcl/partials/sections/gallery.html +12 -0
- package/src/generator/themes/srcl/partials/sections/image.html +6 -0
- package/src/generator/themes/srcl/partials/sections/interactive.html +8 -0
- package/src/generator/themes/srcl/partials/sections/metrics.html +10 -0
- package/src/generator/themes/srcl/partials/sections/outcomes.html +5 -0
- package/src/generator/themes/srcl/partials/sections/overview.html +5 -0
- package/src/generator/themes/srcl/partials/sections/process.html +5 -0
- package/src/generator/themes/srcl/partials/skills.html +21 -0
- package/src/generator/themes/srcl/partials/writing.html +14 -0
- package/src/generator/themes/srcl/script.js +354 -0
- package/src/generator/themes/srcl/styles.css +1260 -0
- package/src/generator/themes/srcl/template.html +46 -0
- package/src/generator/themes/srcl/templates/experiments-index.html +66 -0
- package/src/generator/themes/srcl/templates/homepage.html +136 -0
- package/src/generator/themes/srcl/templates/project.html +96 -0
- package/src/generator/themes/srcl/templates/projects-index.html +70 -0
- package/src/generator/themes/srcl/templates/writing-index.html +61 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<section id="skills" class="section">
|
|
2
|
+
<h2 class="section-title">SKILLS</h2>
|
|
3
|
+
<div class="srcl-data-table">
|
|
4
|
+
<table>
|
|
5
|
+
<thead>
|
|
6
|
+
<tr>
|
|
7
|
+
<th>CATEGORY</th>
|
|
8
|
+
<th>TECHNOLOGIES</th>
|
|
9
|
+
</tr>
|
|
10
|
+
</thead>
|
|
11
|
+
<tbody>
|
|
12
|
+
{{#skillCategories}}
|
|
13
|
+
<tr>
|
|
14
|
+
<td class="category">{{category}}</td>
|
|
15
|
+
<td class="skills">{{skills}}</td>
|
|
16
|
+
</tr>
|
|
17
|
+
{{/skillCategories}}
|
|
18
|
+
</tbody>
|
|
19
|
+
</table>
|
|
20
|
+
</div>
|
|
21
|
+
</section>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<section id="writing" class="section">
|
|
2
|
+
<h2 class="section-title">WRITING</h2>
|
|
3
|
+
<ul class="srcl-list writing-list">
|
|
4
|
+
{{#articles}}
|
|
5
|
+
<li class="writing-item">
|
|
6
|
+
<div class="writing-header">
|
|
7
|
+
<a href="{{url}}" target="_blank" class="writing-title">{{title}}</a>
|
|
8
|
+
<span class="writing-date">{{date}}</span>
|
|
9
|
+
</div>
|
|
10
|
+
{{#description}}<p class="writing-description">{{description}}</p>{{/description}}
|
|
11
|
+
</li>
|
|
12
|
+
{{/articles}}
|
|
13
|
+
</ul>
|
|
14
|
+
</section>
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SRCL Theme Interactive Features
|
|
3
|
+
* Vanilla JavaScript - No dependencies
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
(function() {
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
// ============================================
|
|
10
|
+
// Accordion Functionality
|
|
11
|
+
// ============================================
|
|
12
|
+
function initAccordions() {
|
|
13
|
+
const headers = document.querySelectorAll('.accordion-header');
|
|
14
|
+
|
|
15
|
+
headers.forEach(header => {
|
|
16
|
+
header.addEventListener('click', () => {
|
|
17
|
+
const item = header.parentElement;
|
|
18
|
+
const isExpanded = item.dataset.expanded === 'true';
|
|
19
|
+
|
|
20
|
+
// Close all accordions in the same container
|
|
21
|
+
const container = item.parentElement;
|
|
22
|
+
container.querySelectorAll('.accordion-item').forEach(i => {
|
|
23
|
+
i.dataset.expanded = 'false';
|
|
24
|
+
i.querySelector('.accordion-header')?.setAttribute('aria-expanded', 'false');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Open this one if it was closed
|
|
28
|
+
if (!isExpanded) {
|
|
29
|
+
item.dataset.expanded = 'true';
|
|
30
|
+
header.setAttribute('aria-expanded', 'true');
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Keyboard support
|
|
35
|
+
header.addEventListener('keydown', (e) => {
|
|
36
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
header.click();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ============================================
|
|
45
|
+
// Smooth Scroll Navigation
|
|
46
|
+
// ============================================
|
|
47
|
+
function initSmoothScroll() {
|
|
48
|
+
document.querySelectorAll('a[href^="#"]').forEach(link => {
|
|
49
|
+
link.addEventListener('click', (e) => {
|
|
50
|
+
const href = link.getAttribute('href');
|
|
51
|
+
if (href === '#') return;
|
|
52
|
+
|
|
53
|
+
const target = document.querySelector(href);
|
|
54
|
+
if (target) {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
|
|
57
|
+
// Account for fixed action bar height
|
|
58
|
+
const actionBar = document.querySelector('.srcl-action-bar');
|
|
59
|
+
const offset = actionBar ? actionBar.offsetHeight + 16 : 16;
|
|
60
|
+
|
|
61
|
+
const targetPosition = target.getBoundingClientRect().top + window.scrollY - offset;
|
|
62
|
+
|
|
63
|
+
window.scrollTo({
|
|
64
|
+
top: targetPosition,
|
|
65
|
+
behavior: 'smooth'
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Update URL hash without jumping
|
|
69
|
+
history.pushState(null, '', href);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ============================================
|
|
76
|
+
// Theme Toggle (Dark/Light)
|
|
77
|
+
// ============================================
|
|
78
|
+
function toggleTheme() {
|
|
79
|
+
const current = document.body.dataset.theme || document.documentElement.dataset.theme || 'dark';
|
|
80
|
+
const newTheme = current === 'dark' ? 'light' : 'dark';
|
|
81
|
+
|
|
82
|
+
// Set on both html and body for CSS compatibility
|
|
83
|
+
document.documentElement.dataset.theme = newTheme;
|
|
84
|
+
document.body.dataset.theme = newTheme;
|
|
85
|
+
|
|
86
|
+
// Persist preference
|
|
87
|
+
try {
|
|
88
|
+
localStorage.setItem('srcl-theme', newTheme);
|
|
89
|
+
} catch (e) {
|
|
90
|
+
// localStorage not available
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Announce change for screen readers
|
|
94
|
+
announceChange(`Theme changed to ${newTheme} mode`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function loadThemePreference() {
|
|
98
|
+
try {
|
|
99
|
+
const saved = localStorage.getItem('srcl-theme');
|
|
100
|
+
if (saved) {
|
|
101
|
+
// Set on both html and body for CSS compatibility
|
|
102
|
+
document.documentElement.dataset.theme = saved;
|
|
103
|
+
document.body.dataset.theme = saved;
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
// localStorage not available
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ============================================
|
|
111
|
+
// Grid Toggle
|
|
112
|
+
// ============================================
|
|
113
|
+
function toggleGrid() {
|
|
114
|
+
const grid = document.querySelector('.grid-overlay');
|
|
115
|
+
if (grid) {
|
|
116
|
+
const isVisible = grid.style.display !== 'none';
|
|
117
|
+
grid.style.display = isVisible ? 'none' : 'block';
|
|
118
|
+
|
|
119
|
+
// Persist preference
|
|
120
|
+
try {
|
|
121
|
+
localStorage.setItem('srcl-grid', isVisible ? 'hidden' : 'visible');
|
|
122
|
+
} catch (e) {
|
|
123
|
+
// localStorage not available
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
announceChange(`Grid overlay ${isVisible ? 'hidden' : 'visible'}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function loadGridPreference() {
|
|
131
|
+
try {
|
|
132
|
+
const saved = localStorage.getItem('srcl-grid');
|
|
133
|
+
const grid = document.querySelector('.grid-overlay');
|
|
134
|
+
if (grid && saved === 'hidden') {
|
|
135
|
+
grid.style.display = 'none';
|
|
136
|
+
}
|
|
137
|
+
} catch (e) {
|
|
138
|
+
// localStorage not available
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ============================================
|
|
143
|
+
// Keyboard Shortcuts
|
|
144
|
+
// ============================================
|
|
145
|
+
function initKeyboardShortcuts() {
|
|
146
|
+
document.addEventListener('keydown', (e) => {
|
|
147
|
+
// Don't trigger shortcuts when typing in inputs
|
|
148
|
+
if (e.target.matches('input, textarea, select')) return;
|
|
149
|
+
|
|
150
|
+
// Ctrl+T or Cmd+T: Toggle theme
|
|
151
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 't') {
|
|
152
|
+
e.preventDefault();
|
|
153
|
+
toggleTheme();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Ctrl+G or Cmd+G: Toggle grid
|
|
157
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'g') {
|
|
158
|
+
e.preventDefault();
|
|
159
|
+
toggleGrid();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Number keys 1-9: Jump to sections
|
|
163
|
+
if (!e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
164
|
+
const num = parseInt(e.key);
|
|
165
|
+
if (num >= 1 && num <= 9) {
|
|
166
|
+
const sections = document.querySelectorAll('.section');
|
|
167
|
+
const target = sections[num - 1];
|
|
168
|
+
if (target) {
|
|
169
|
+
e.preventDefault();
|
|
170
|
+
target.scrollIntoView({ behavior: 'smooth' });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Escape: Close any open accordion
|
|
176
|
+
if (e.key === 'Escape') {
|
|
177
|
+
document.querySelectorAll('.accordion-item[data-expanded="true"]').forEach(item => {
|
|
178
|
+
item.dataset.expanded = 'false';
|
|
179
|
+
item.querySelector('.accordion-header')?.setAttribute('aria-expanded', 'false');
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Hotkey button click handlers
|
|
185
|
+
document.querySelectorAll('.hotkey[data-hotkey]').forEach(button => {
|
|
186
|
+
button.addEventListener('click', () => {
|
|
187
|
+
const hotkey = button.dataset.hotkey;
|
|
188
|
+
if (hotkey === 'ctrl+t') toggleTheme();
|
|
189
|
+
if (hotkey === 'ctrl+g') toggleGrid();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ============================================
|
|
195
|
+
// Active Section Highlight on Scroll
|
|
196
|
+
// ============================================
|
|
197
|
+
function initScrollSpy() {
|
|
198
|
+
const sections = document.querySelectorAll('.section[id], #hero');
|
|
199
|
+
const navLinks = document.querySelectorAll('.srcl-action-bar .primary a[href^="#"]');
|
|
200
|
+
|
|
201
|
+
if (sections.length === 0 || navLinks.length === 0) return;
|
|
202
|
+
|
|
203
|
+
let ticking = false;
|
|
204
|
+
|
|
205
|
+
function updateActiveSection() {
|
|
206
|
+
const scrollPos = window.scrollY + 100; // Offset for header
|
|
207
|
+
|
|
208
|
+
let currentSection = '';
|
|
209
|
+
|
|
210
|
+
sections.forEach(section => {
|
|
211
|
+
const sectionTop = section.offsetTop;
|
|
212
|
+
const sectionHeight = section.offsetHeight;
|
|
213
|
+
|
|
214
|
+
if (scrollPos >= sectionTop && scrollPos < sectionTop + sectionHeight) {
|
|
215
|
+
currentSection = section.getAttribute('id');
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
navLinks.forEach(link => {
|
|
220
|
+
const href = link.getAttribute('href').substring(1);
|
|
221
|
+
if (href === currentSection) {
|
|
222
|
+
link.classList.add('active');
|
|
223
|
+
} else {
|
|
224
|
+
link.classList.remove('active');
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
ticking = false;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
window.addEventListener('scroll', () => {
|
|
232
|
+
if (!ticking) {
|
|
233
|
+
requestAnimationFrame(updateActiveSection);
|
|
234
|
+
ticking = true;
|
|
235
|
+
}
|
|
236
|
+
}, { passive: true });
|
|
237
|
+
|
|
238
|
+
// Initial check
|
|
239
|
+
updateActiveSection();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ============================================
|
|
243
|
+
// Cursor Blink Effect
|
|
244
|
+
// ============================================
|
|
245
|
+
function initCursorBlink() {
|
|
246
|
+
// Add blinking cursor to hero name
|
|
247
|
+
const heroName = document.querySelector('.hero-card .name');
|
|
248
|
+
if (heroName) {
|
|
249
|
+
const cursor = document.createElement('span');
|
|
250
|
+
cursor.className = 'blink-cursor';
|
|
251
|
+
cursor.textContent = '_';
|
|
252
|
+
cursor.setAttribute('aria-hidden', 'true');
|
|
253
|
+
heroName.appendChild(cursor);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Add CSS for cursor if not already present
|
|
257
|
+
if (!document.querySelector('#srcl-cursor-style')) {
|
|
258
|
+
const style = document.createElement('style');
|
|
259
|
+
style.id = 'srcl-cursor-style';
|
|
260
|
+
style.textContent = `
|
|
261
|
+
.blink-cursor {
|
|
262
|
+
animation: cursor-blink 1s step-end infinite;
|
|
263
|
+
margin-left: 0.25ch;
|
|
264
|
+
}
|
|
265
|
+
@keyframes cursor-blink {
|
|
266
|
+
0%, 50% { opacity: 1; }
|
|
267
|
+
51%, 100% { opacity: 0; }
|
|
268
|
+
}
|
|
269
|
+
`;
|
|
270
|
+
document.head.appendChild(style);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ============================================
|
|
275
|
+
// Active nav link styles
|
|
276
|
+
// ============================================
|
|
277
|
+
function initNavStyles() {
|
|
278
|
+
// Add CSS for active nav links if not present
|
|
279
|
+
if (!document.querySelector('#srcl-nav-style')) {
|
|
280
|
+
const style = document.createElement('style');
|
|
281
|
+
style.id = 'srcl-nav-style';
|
|
282
|
+
style.textContent = `
|
|
283
|
+
.srcl-action-bar .primary a.active {
|
|
284
|
+
background: var(--theme-focused);
|
|
285
|
+
}
|
|
286
|
+
`;
|
|
287
|
+
document.head.appendChild(style);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ============================================
|
|
292
|
+
// Accessibility: Screen Reader Announcements
|
|
293
|
+
// ============================================
|
|
294
|
+
let announcer = null;
|
|
295
|
+
|
|
296
|
+
function announceChange(message) {
|
|
297
|
+
if (!announcer) {
|
|
298
|
+
announcer = document.createElement('div');
|
|
299
|
+
announcer.setAttribute('aria-live', 'polite');
|
|
300
|
+
announcer.setAttribute('aria-atomic', 'true');
|
|
301
|
+
announcer.className = 'sr-only';
|
|
302
|
+
announcer.style.cssText = `
|
|
303
|
+
position: absolute;
|
|
304
|
+
width: 1px;
|
|
305
|
+
height: 1px;
|
|
306
|
+
padding: 0;
|
|
307
|
+
margin: -1px;
|
|
308
|
+
overflow: hidden;
|
|
309
|
+
clip: rect(0, 0, 0, 0);
|
|
310
|
+
white-space: nowrap;
|
|
311
|
+
border: 0;
|
|
312
|
+
`;
|
|
313
|
+
document.body.appendChild(announcer);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
announcer.textContent = message;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ============================================
|
|
320
|
+
// Initialize Everything
|
|
321
|
+
// ============================================
|
|
322
|
+
function init() {
|
|
323
|
+
loadThemePreference();
|
|
324
|
+
loadGridPreference();
|
|
325
|
+
initAccordions();
|
|
326
|
+
initSmoothScroll();
|
|
327
|
+
initKeyboardShortcuts();
|
|
328
|
+
initScrollSpy();
|
|
329
|
+
initCursorBlink();
|
|
330
|
+
initNavStyles();
|
|
331
|
+
|
|
332
|
+
// Log available shortcuts
|
|
333
|
+
console.log('%c[SRCL] Keyboard shortcuts:', 'color: #ef6300; font-weight: bold');
|
|
334
|
+
console.log(' Ctrl+T: Toggle dark/light theme');
|
|
335
|
+
console.log(' Ctrl+G: Toggle grid overlay');
|
|
336
|
+
console.log(' 1-9: Jump to sections');
|
|
337
|
+
console.log(' Esc: Close accordions');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Run on DOM ready
|
|
341
|
+
if (document.readyState === 'loading') {
|
|
342
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
343
|
+
} else {
|
|
344
|
+
init();
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Expose functions globally for debugging
|
|
348
|
+
window.SRCL = {
|
|
349
|
+
toggleTheme,
|
|
350
|
+
toggleGrid,
|
|
351
|
+
version: '1.0.0'
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
})();
|