Skip to content

Commit fc44e04

Browse files
committed
Initial Commit
1 parent 3d9543b commit fc44e04

3 files changed

Lines changed: 102 additions & 39 deletions

File tree

index.html

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
"@type": "Person",
3535
"name": "Muhammed Rashid",
3636
"url": "https://devmdrd.github.io/",
37-
"email": "mdrd.muhammedrashid@gmail.com",
3837
"jobTitle": "Full Stack Developer",
3938
"image": "https://devmdrd.github.io/assets/images/profile.webp",
4039
"knowsAbout": ["React", "Angular", "Node.js", "TypeScript", "MongoDB", "MySQL"],
@@ -367,7 +366,7 @@ <h2 class="section-title"><i class="fa-solid fa-folder-open"></i>projects</h2>
367366
</button>
368367
</div>
369368

370-
<div id="projects-count" class="projects-count" hidden></div>
369+
<div id="projects-count" class="projects-count" aria-live="polite" hidden></div>
371370
</div>
372371

373372
<div class="snap-scroll-body">
@@ -586,9 +585,12 @@ <h2 class="section-title" id="contact-title">
586585
novalidate
587586
aria-labelledby="contact-title"
588587
class="contact-form">
589-
<input type="text" name="name" placeholder="your name" autocomplete="name" required class="form-field" aria-label="Full name" />
590-
<input type="email" name="email" placeholder="email address" autocomplete="email" required class="form-field" aria-label="Email address" />
591-
<textarea name="message" placeholder="your message ..." rows="4" required class="form-field form-textarea" aria-label="Message"></textarea>
588+
<label for="contact-name" class="sr-only">Full name</label>
589+
<input id="contact-name" type="text" name="name" placeholder="your name" autocomplete="name" required class="form-field" />
590+
<label for="contact-email" class="sr-only">Email address</label>
591+
<input id="contact-email" type="email" name="email" placeholder="email address" autocomplete="email" required class="form-field" />
592+
<label for="contact-message" class="sr-only">Message</label>
593+
<textarea id="contact-message" name="message" placeholder="your message ..." rows="4" required class="form-field form-textarea"></textarea>
592594
<button id="contact-submit" type="submit" class="filled-btn contact-submit">
593595
<i class="fa-solid fa-paper-plane"></i>
594596
<span class="btn-text">send message</span>

main.js

Lines changed: 82 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,20 @@
8585
}
8686

8787
var user = await uRes.json();
88+
if (!rRes.ok) throw new Error(`repos_http_${rRes.status}`);
8889
var list = await rRes.json();
8990
if (!Array.isArray(list)) throw new Error('repos_failed');
9091

9192
var nonFork = list.filter(function (r) { return !r.fork; });
9293
var stats = {
93-
followers: user.followers ?? 0,
94-
following: user.following ?? 0,
94+
followers: Number(user.followers) || 0,
95+
following: Number(user.following) || 0,
9596
repos: nonFork.length,
9697
stars: list.reduce(function (s, r) { return s + (r.stargazers_count ?? 0); }, 0),
97-
gists: user.public_gists ?? 0,
98-
joinedAt: user.created_at,
99-
hireable: user.hireable ?? false,
100-
bio: user.bio ?? '',
98+
gists: Number(user.public_gists) || 0,
99+
joinedAt: user.created_at ?? null,
100+
hireable: Boolean(user.hireable),
101+
bio: String(user.bio ?? ''),
101102
};
102103

103104
var langResults = await Promise.allSettled(
@@ -139,6 +140,7 @@
139140

140141
/* ── Fetch: contributions ──────────────────────────────────────── */
141142
function computeStreak(flat) {
143+
if (!Array.isArray(flat) || !flat.length) return { current: 0, longest: 0 };
142144
var now = new Date();
143145
var today = [
144146
now.getFullYear(),
@@ -174,7 +176,11 @@
174176
if (!res.ok) throw new Error('contrib_error');
175177

176178
var json = await res.json();
177-
var flat = json.contributions ?? [];
179+
var flat = Array.isArray(json.contributions)
180+
? json.contributions.filter(function (e) {
181+
return e && typeof e.date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(e.date);
182+
})
183+
: [];
178184

179185
var weeksMap = {};
180186
flat.forEach(function (e) {
@@ -239,9 +245,9 @@
239245
var theme = (stored === 'dark' || stored === 'light') ? stored
240246
: (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
241247
applyTheme(theme);
242-
document.getElementById('theme-toggle')?.addEventListener('click', function () {
248+
document.getElementById('theme-toggle')?.addEventListener('click', debounce(function () {
243249
applyTheme(getTheme() === 'dark' ? 'light' : 'dark');
244-
});
250+
}, 200));
245251
}
246252

247253
/* ── Contact form ──────────────────────────────────────────────── */
@@ -286,6 +292,14 @@
286292
e.preventDefault();
287293
clearTimeout(clearTimer);
288294
if (feedback) feedback.hidden = true;
295+
296+
if (!form.checkValidity()) {
297+
form.querySelectorAll('[required]').forEach(function (field) {
298+
field.classList.toggle('is-invalid', !field.validity.valid);
299+
});
300+
return;
301+
}
302+
289303
setSending(true);
290304
try {
291305
var res = await fetch(FORMSPREE_ENDPOINT, {
@@ -555,7 +569,13 @@
555569
icon.className = 'fa-solid fa-check';
556570
setTimeout(function () { icon.className = 'fa-regular fa-copy'; }, 1500);
557571
}
558-
}).catch(function () {});
572+
}).catch(function () {
573+
var icon = copyBtn.querySelector('i');
574+
if (icon) {
575+
icon.className = 'fa-solid fa-xmark';
576+
setTimeout(function () { icon.className = 'fa-regular fa-copy'; }, 1500);
577+
}
578+
});
559579
return;
560580
}
561581
if (e.target.closest('.card-flip-btn')) { e.target.closest('[data-npm]')?.classList.add('flipped'); return; }
@@ -812,6 +832,7 @@
812832
}
813833

814834
function renderExpDetail(idx) {
835+
if (idx < 0 || idx >= EXP_DATA.length) return;
815836
var d = EXP_DATA[idx];
816837
var detail = document.getElementById('exp-detail');
817838
if (!detail) return;
@@ -866,8 +887,11 @@
866887

867888
tabs.forEach(function (tab, i) {
868889
tab.id = `exp-tab-${tab.dataset.idx}`;
869-
var dateEl = tab.querySelector('.exp-tab__date');
870-
if (dateEl) dateEl.textContent = calcDuration(EXP_DATA[Number(tab.dataset.idx)].period);
890+
var dateEl = tab.querySelector('.exp-tab__date');
891+
var expIdx = Number(tab.dataset.idx);
892+
if (dateEl && expIdx >= 0 && expIdx < EXP_DATA.length) {
893+
dateEl.textContent = calcDuration(EXP_DATA[expIdx].period);
894+
}
871895
tab.addEventListener('click', function () { activate(i); });
872896
tab.addEventListener('keydown', function (e) {
873897
var next = null;
@@ -1164,19 +1188,28 @@
11641188
var k = `${dt.getFullYear()}-${dt.getMonth()}`;
11651189
if (!seen.has(k) && dt.getDate() <= 7) {
11661190
seen.add(k);
1167-
monthsEl.insertAdjacentHTML('beforeend',
1168-
`<span class="contrib-chart__month" style="left:${wi * CELL_STEP}px">${dt.toLocaleString('default', { month: 'short' })}</span>`
1169-
);
1191+
var mSpan = document.createElement('span');
1192+
mSpan.className = 'contrib-chart__month';
1193+
mSpan.style.left = (wi * CELL_STEP) + 'px';
1194+
mSpan.textContent = dt.toLocaleString('default', { month: 'short' });
1195+
monthsEl.appendChild(mSpan);
11701196
}
11711197
});
11721198
});
11731199
}
11741200

11751201
var daysEl = content.querySelector('.contrib-chart__day-labels');
11761202
if (daysEl) {
1177-
daysEl.innerHTML = DAY_LABELS.map(function (label, i) {
1178-
return `<div class="contrib-chart__day" style="height:${CELL_SIZE}px;margin-bottom:${i === 6 ? 0 : CELL_GAP}px">${label}</div>`;
1179-
}).join('');
1203+
var dayFrag = document.createDocumentFragment();
1204+
DAY_LABELS.forEach(function (label, i) {
1205+
var div = document.createElement('div');
1206+
div.className = 'contrib-chart__day';
1207+
div.style.height = CELL_SIZE + 'px';
1208+
div.style.marginBottom = (i === 6 ? 0 : CELL_GAP) + 'px';
1209+
div.textContent = label;
1210+
dayFrag.appendChild(div);
1211+
});
1212+
daysEl.replaceChildren(dayFrag);
11801213
}
11811214

11821215
var innerEl = content.querySelector('.contrib-chart__scroll > div');
@@ -1186,23 +1219,41 @@
11861219
if (grid) {
11871220
var total = weeks.flat().reduce(function (s, d) { return s + (d?.count ?? 0); }, 0);
11881221
grid.setAttribute('aria-label', `Contributions ${year}${total} total`);
1189-
grid.innerHTML = weeks.map(function (w) {
1190-
return w.map(function (d) {
1191-
if (!d) return '<div class="cell"></div>';
1192-
var date = String(d.date).replace(/[^0-9-]/g, '');
1193-
var count = Number(d.count);
1194-
return `<div class="cell contrib-cell ${d.disabled ? 'is-disabled' : 'is-active'}" style="background-color:${contribColor(count, theme)}" data-date="${date}" data-count="${count}"></div>`;
1195-
}).join('');
1196-
}).join('');
1222+
var gridFrag = document.createDocumentFragment();
1223+
weeks.forEach(function (w) {
1224+
w.forEach(function (d) {
1225+
var cell = document.createElement('div');
1226+
if (!d) {
1227+
cell.className = 'cell';
1228+
} else {
1229+
var count = Number(d.count);
1230+
cell.className = 'cell contrib-cell ' + (d.disabled ? 'is-disabled' : 'is-active');
1231+
cell.style.backgroundColor = contribColor(count, theme);
1232+
cell.dataset.date = String(d.date).replace(/[^0-9-]/g, '');
1233+
cell.dataset.count = String(count);
1234+
}
1235+
gridFrag.appendChild(cell);
1236+
});
1237+
});
1238+
grid.replaceChildren(gridFrag);
11971239
}
11981240

11991241
var legendEl = content.querySelector('.contrib-legend');
12001242
if (legendEl) {
1201-
var less = legendEl.querySelector('.legend-less')?.outerHTML ?? '';
1202-
var more = legendEl.querySelector('.legend-more')?.outerHTML ?? '';
1203-
legendEl.innerHTML = less
1204-
+ LEGEND_COUNTS.map(function (v) { return `<div class="cell" style="background-color:${contribColor(v, theme)}"></div>`; }).join('')
1205-
+ more;
1243+
var lessNode = legendEl.querySelector('.legend-less');
1244+
var moreNode = legendEl.querySelector('.legend-more');
1245+
var lessClone = lessNode ? lessNode.cloneNode(true) : null;
1246+
var moreClone = moreNode ? moreNode.cloneNode(true) : null;
1247+
var legFrag = document.createDocumentFragment();
1248+
if (lessClone) legFrag.appendChild(lessClone);
1249+
LEGEND_COUNTS.forEach(function (v) {
1250+
var cell = document.createElement('div');
1251+
cell.className = 'cell';
1252+
cell.style.backgroundColor = contribColor(v, theme);
1253+
legFrag.appendChild(cell);
1254+
});
1255+
if (moreClone) legFrag.appendChild(moreClone);
1256+
legendEl.replaceChildren(legFrag);
12061257
}
12071258
}
12081259

styles.css

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,16 @@ button, input, textarea, select { font: inherit; }
7676
}
7777

7878
input:focus-visible,
79-
textarea:focus-visible,
79+
textarea:focus-visible {
80+
outline: 1px solid var(--fg);
81+
outline-offset: 2px;
82+
box-shadow: none;
83+
}
84+
8085
select:focus,
8186
select:focus-visible {
8287
outline: none;
83-
box-shadow: none;
88+
border-color: var(--fg);
8489
}
8590

8691
[hidden] { display: none !important; }
@@ -207,6 +212,12 @@ hr {
207212
flex-shrink: 0;
208213
}
209214

215+
.dot-nav__btn::after {
216+
content: '';
217+
position: absolute;
218+
inset: -12px;
219+
}
220+
210221
.dot-nav__btn::before {
211222
content: attr(data-section);
212223
position: absolute;
@@ -1754,7 +1765,6 @@ i.projects-search-icon,
17541765
i.skills-search-icon,
17551766
.skill-card__icon,
17561767
.about-intro .fa-angle-right,
1757-
.about-info-row i[class*="fa-"],
17581768
.project-card__featured i[class*="fa-"],
17591769
.project-card__stats i[class*="fa-"],
17601770
.streak-cell > i[class*="fa-"] { color: var(--fg-secondary); }

0 commit comments

Comments
 (0)