Skip to content

Commit 91e3107

Browse files
author
TechStack Global
committed
fix: final stability fixes — favicon, nav, scripts, affiliates, responsive
fix(favicon): normalize favicon links sitewide (use canonical header block)
fix(nav): restore canonical header/nav on pillar & policy pages
fix(scripts): add main site script reference to missing pages
fix(affiliates): add missing tag to amazon links & normalize rel and target
fix(responsive): wrap tables, make images responsive, remove fixed widths
fix(links): add pillar & review contextual links to odyssey vs alienware comparison
1 parent bdf0196 commit 91e3107

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2184
-183
lines changed

affiliate-disclosure.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
</ul>
4545
</nav>
4646
</header>
47-
<main class="container section-padding" style="margin-top: 80px; max-width: 800px;">
47+
<main class="container section-padding" style="margin-top: 80px; max-max-width: 100%; height: auto;">
4848
<div class="glass-panel" style="padding: 4rem;">
4949
<h1 style="font-size: 2.5rem; margin-bottom: 2rem;">Affiliate <span class="accent">Disclosure</span></h1>
5050
<p style="font-size: 1.1rem; color: var( text-secondary); margin-bottom: 1.5rem;">At TechStack Global, we

affiliate_report.csv

Lines changed: 75 additions & 0 deletions
Large diffs are not rendered by default.

amazon-stack.html

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,15 @@ <h1>The Amazon Stack</h1>
9595
style="margin-bottom: 0.5rem; border-bottom: 1px solid var( accent); display: inline-block; padding-bottom: 5px;">
9696
Product Library
9797
</h2>
98-
<p style="color: var( text-muted); margin-bottom: 2rem; max-width: 800px;">
98+
<p style="color: var( text-muted); margin-bottom: 2rem; max-max-width: 100%; height: auto;">
9999
Hardware chosen for performance, reliability, and practical daily use.
100100
</p>
101101
<div class="blog-grid" style="grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));">
102102

103103
<!-- Bose QuietComfort Ultra -->
104104
<div class="product-item glass-card">
105105
<div class="product-thumbnail-wrapper">
106-
<img alt="Bose QuietComfort Ultra wireless headphones thumbnail" class="product-thumbnail"
106+
<img style="max-width: 100%; height: auto;" alt="Bose QuietComfort Ultra wireless headphones thumbnail" class="product-thumbnail"
107107
loading="lazy" src="posts/images/bose-qc-ultra-front.jpg" />
108108
</div>
109109
<div class="product-info">
@@ -117,7 +117,7 @@ <h3><a aria-label="Open review: Bose QuietComfort Ultra"
117117
<!-- Sony WH-1000XM5 -->
118118
<div class="product-item glass-card">
119119
<div class="product-thumbnail-wrapper">
120-
<img alt="Sony WH-1000XM5 wireless noise cancelling headphones thumbnail"
120+
<img style="max-width: 100%; height: auto;" alt="Sony WH-1000XM5 wireless noise cancelling headphones thumbnail"
121121
class="product-thumbnail" loading="lazy" src="posts/images/sony-wh-1000xm5-front.jpg" />
122122
</div>
123123
<div class="product-info">
@@ -132,7 +132,7 @@ <h3><a aria-label="Open review: Sony WH-1000XM5" href="posts/sony-wh-1000xm5-rev
132132
<!-- Samsung Odyssey G8 -->
133133
<div class="product-item glass-card">
134134
<div class="product-thumbnail-wrapper">
135-
<img alt="Samsung Odyssey G8 OLED monitor front view thumbnail" class="product-thumbnail"
135+
<img style="max-width: 100%; height: auto;" alt="Samsung Odyssey G8 OLED monitor front view thumbnail" class="product-thumbnail"
136136
loading="lazy" src="posts/images/odyssey-g8-front.jpg" />
137137
</div>
138138
<div class="product-info">
@@ -146,7 +146,7 @@ <h3><a aria-label="Open review: Samsung Odyssey G8 OLED"
146146
<!-- Alienware AW3423DWF -->
147147
<div class="product-item glass-card">
148148
<div class="product-thumbnail-wrapper">
149-
<img alt="Dell Alienware AW3423DWF QD-OLED monitor front view thumbnail"
149+
<img style="max-width: 100%; height: auto;" alt="Dell Alienware AW3423DWF QD-OLED monitor front view thumbnail"
150150
class="product-thumbnail" loading="lazy" src="posts/images/alienware-aw3423dwf-front.jpg" />
151151
</div>
152152
<div class="product-info">
@@ -160,7 +160,7 @@ <h3><a aria-label="Open review: Dell Alienware AW3423DWF"
160160
<!-- Shure SM7dB-->
161161
<div class="product-item glass-card">
162162
<div class="product-thumbnail-wrapper">
163-
<img alt="Shure SM7dB dynamic vocal microphone product thumbnail" class="product-thumbnail"
163+
<img style="max-width: 100%; height: auto;" alt="Shure SM7dB dynamic vocal microphone product thumbnail" class="product-thumbnail"
164164
loading="lazy" src="posts/images/shure-sm7db-primary.jpg" />
165165
</div>
166166
<div class="product-info">
@@ -176,7 +176,7 @@ <h3><a aria-label="Open review: Shure SM7dB Dynamic Microphone"
176176
<!-- Shure SM7B-->
177177
<div class="product-item glass-card">
178178
<div class="product-thumbnail-wrapper">
179-
<img alt="Shure SM7B dynamic studio microphone profile view thumbnail" class="product-thumbnail"
179+
<img style="max-width: 100%; height: auto;" alt="Shure SM7B dynamic studio microphone profile view thumbnail" class="product-thumbnail"
180180
loading="lazy" src="posts/images/shure-sm7b-primary.jpg" />
181181
</div>
182182
<div class="product-info">
@@ -191,7 +191,7 @@ <h3><a aria-label="Open review: Shure SM7B" href="posts/shure-sm7b-review.html">
191191
<!-- MacBook Pro-->
192192
<div class="product-item glass-card">
193193
<div class="product-thumbnail-wrapper">
194-
<img alt="Apple MacBook Pro M4 Pro product thumbnail" class="product-thumbnail" loading="lazy"
194+
<img style="max-width: 100%; height: auto;" alt="Apple MacBook Pro M4 Pro product thumbnail" class="product-thumbnail" loading="lazy"
195195
src="assets/images/macbook_hero.jpg" />
196196
</div>
197197
<div class="product-info">
@@ -205,7 +205,7 @@ <h3><a aria-label="Open review: Apple MacBook Pro M4 Pro"
205205
<!-- Dell XPS 15-->
206206
<div class="product-item glass-card">
207207
<div class="product-thumbnail-wrapper">
208-
<img alt="Dell XPS 15 9530 product thumbnail" class="product-thumbnail" loading="lazy"
208+
<img style="max-width: 100%; height: auto;" alt="Dell XPS 15 9530 product thumbnail" class="product-thumbnail" loading="lazy"
209209
src="assets/images/products/61Ks9X44eVL._AC_SL1181_.jpg" />
210210
</div>
211211
<div class="product-info">
@@ -219,7 +219,7 @@ <h3><a aria-label="Open review: Dell XPS 15 9530" href="posts/dell-xps-15-9530-r
219219
<!-- Surface Laptop Studio 2-->
220220
<div class="product-item glass-card">
221221
<div class="product-thumbnail-wrapper">
222-
<img alt="Surface Laptop Studio 2 product thumbnail" class="product-thumbnail" loading="lazy"
222+
<img style="max-width: 100%; height: auto;" alt="Surface Laptop Studio 2 product thumbnail" class="product-thumbnail" loading="lazy"
223223
src="assets/images/products/61LMopCIyTL._AC_SL1200_.jpg" />
224224
</div>
225225
<div class="product-info">
@@ -234,7 +234,7 @@ <h3><a aria-label="Open review: Surface Laptop Studio 2"
234234
<!-- Samsung SSD-->
235235
<div class="product-item glass-card">
236236
<div class="product-thumbnail-wrapper">
237-
<img alt="Samsung 990 PRO SSD 2TB product thumbnail" class="product-thumbnail" loading="lazy"
237+
<img style="max-width: 100%; height: auto;" alt="Samsung 990 PRO SSD 2TB product thumbnail" class="product-thumbnail" loading="lazy"
238238
src="assets/images/samsung-990-pro-primary-v3.jpg?v=final_v3" />
239239
</div>
240240
<div class="product-info">
@@ -249,7 +249,7 @@ <h3><a aria-label="Open review: Samsung 990 PRO SSD 2TB"
249249
<!-- LG Monitor-->
250250
<div class="product-item glass-card">
251251
<div class="product-thumbnail-wrapper">
252-
<img alt="LG 27US500-W 4K Monitor product thumbnail" class="product-thumbnail" loading="lazy"
252+
<img style="max-width: 100%; height: auto;" alt="LG 27US500-W 4K Monitor product thumbnail" class="product-thumbnail" loading="lazy"
253253
src="assets/images/products/lg-27us500w-primary.jpg" />
254254
</div>
255255
<div class="product-info">
@@ -264,7 +264,7 @@ <h3><a aria-label="Open review: LG 27US500-W 4K Monitor"
264264
<!-- Dell Dock-->
265265
<div class="product-item glass-card">
266266
<div class="product-thumbnail-wrapper">
267-
<img alt="Dell SD25TB4 Pro Dock product thumbnail" class="product-thumbnail" loading="lazy"
267+
<img style="max-width: 100%; height: auto;" alt="Dell SD25TB4 Pro Dock product thumbnail" class="product-thumbnail" loading="lazy"
268268
src="assets/images/products/dell-sd25tb4-box.jpg" />
269269
</div>
270270
<div class="product-info">

apply_fixes.js

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
const faviconFiles = [
5+
'posts/best-laptops-for-students-2026.html',
6+
'posts/best-premium-laptop-for-work-2026.html',
7+
'posts/best-remote-work-setup-2026.html',
8+
'posts/budget-laptops-under-1000.html',
9+
'posts/do-you-need-thunderbolt-dock.html',
10+
'posts/is-a-4k-monitor-worth-it.html',
11+
'posts/is-samsung-990-pro-worth-it.html',
12+
'posts/samsung-odyssey-g8-vs-alienware-aw3423dwf.html'
13+
];
14+
15+
const headerFiles = [
16+
'posts/best-laptops-for-students-2026.html',
17+
'posts/budget-laptops-under-1000.html',
18+
'privacy-policy.html',
19+
'terms-of-service.html'
20+
];
21+
22+
const missingScriptFiles = [
23+
'about.html',
24+
'affiliate-disclosure.html',
25+
'amazon-stack.html',
26+
'blog.html',
27+
'contact.html',
28+
'index.html',
29+
'privacy-policy.html',
30+
'smart-tools.html',
31+
'terms-of-service.html',
32+
'thank-you.html'
33+
];
34+
35+
const mobileRiskFiles = [
36+
'affiliate-disclosure.html', 'amazon-stack.html', 'contact.html', 'index.html',
37+
'posts/alienware-aw3423dwf-review.html', 'posts/alienware-aw3423dwf-vs-odyssey-g8.html',
38+
'posts/apple-macbook-pro-m4-pro-review.html', 'posts/best-laptops-for-students-2026.html',
39+
'posts/best-premium-laptop-for-work-2026.html', 'posts/budget-laptops-under-1000.html',
40+
'posts/dell-xps-15-9530-review.html', 'posts/do-you-need-thunderbolt-dock.html',
41+
'posts/is-a-4k-monitor-worth-it.html', 'posts/samsung-990-pro-ssd-review.html',
42+
'posts/samsung-odyssey-g8-review.html', 'posts/shure-sm7b-review.html',
43+
'posts/shure-sm7b-vs-sm7db.html', 'posts/shure-sm7db-review.html',
44+
'posts/surface-laptop-studio-2-review.html', 'privacy-policy.html',
45+
'smart-tools.html', 'terms-of-service.html', 'thank-you.html'
46+
];
47+
48+
function getHtmlFiles(dir, files_) {
49+
files_ = files_ || [];
50+
let files = fs.readdirSync(dir);
51+
for (let file of files) {
52+
let name = path.join(dir, file);
53+
if (fs.statSync(name).isDirectory()) {
54+
if (!['.git', 'node_modules', 'images', 'assets'].includes(file)) {
55+
getHtmlFiles(name, files_);
56+
}
57+
} else if (name.endsWith('.html') && !name.startsWith('old_')) {
58+
files_.push(name);
59+
}
60+
}
61+
return files_;
62+
}
63+
64+
const indexContent = fs.readFileSync('index.html', 'utf8');
65+
66+
// 1. Favicon Fix
67+
const faviconBlockMatches = indexContent.match(/<link href="assets\/icons\/favicon-32\.png\?v=6"[\s\S]*?<link href="assets\/icons\/favicon\.ico\?v=6" rel="shortcut icon" \/>/);
68+
if (faviconBlockMatches) {
69+
const canonicalFaviconBlock = faviconBlockMatches[0];
70+
faviconFiles.forEach(file => {
71+
let p = file;
72+
if (fs.existsSync(p)) {
73+
let content = fs.readFileSync(p, 'utf8');
74+
75+
let relativeFaviconBlock = file.includes('posts/') ? canonicalFaviconBlock.replace(/href="assets\//g, 'href="../assets/') : canonicalFaviconBlock;
76+
77+
content = content.replace(/<link[^>]+favicon[^>]+>/gi, '');
78+
content = content.replace(/<link[^>]+apple-touch-icon[^>]+>/gi, '');
79+
content = content.replace(/(<\/head>)/i, `${relativeFaviconBlock}\n$1`);
80+
81+
fs.writeFileSync(p, content);
82+
console.log(`[Favicon] Fixed ${p}`);
83+
}
84+
});
85+
}
86+
87+
// 2. Header Fix
88+
const headerMatches = indexContent.match(/<header class="glass-header">[\s\S]*?<\/header>/);
89+
if (headerMatches) {
90+
const canonicalHeaderBlock = headerMatches[0];
91+
headerFiles.forEach(file => {
92+
let p = file;
93+
if (fs.existsSync(p)) {
94+
let content = fs.readFileSync(p, 'utf8');
95+
96+
let relativeHeaderBlock = canonicalHeaderBlock;
97+
if (file.includes('posts/')) {
98+
relativeHeaderBlock = relativeHeaderBlock.replace(/href="([a-zA-Z0-9-]+\.html)"/g, 'href="../$1"');
99+
}
100+
101+
let existingHeaderMatch = content.match(/<header[^>]*>[\s\S]*?<\/header>/i);
102+
if (existingHeaderMatch) {
103+
content = content.replace(existingHeaderMatch[0], relativeHeaderBlock);
104+
} else {
105+
content = content.replace(/(<body[^>]*>)/i, `$1\n ${relativeHeaderBlock}`);
106+
}
107+
108+
fs.writeFileSync(p, content);
109+
console.log(`[Header] Fixed ${p}`);
110+
}
111+
});
112+
}
113+
114+
// 3. Scripts Fix
115+
missingScriptFiles.forEach(file => {
116+
let p = file;
117+
if (fs.existsSync(p)) {
118+
let content = fs.readFileSync(p, 'utf8');
119+
if (!content.match(/<script[^>]+script\.js"/i)) {
120+
let scriptTag = file.includes('posts/') ? '<script src="../script.js"></script>' : '<script src="script.js"></script>';
121+
content = content.replace(/(<\/body>)/i, ` ${scriptTag}\n$1`);
122+
fs.writeFileSync(p, content);
123+
console.log(`[Script] Added to ${p}`);
124+
}
125+
}
126+
});
127+
128+
// 4. Affiliate Links Fix & Tracker report
129+
let csvContent = "File,Old URL,New URL,Old Rel,New Rel,Old Target,New Target\n";
130+
const allHtmlFiles = getHtmlFiles('.');
131+
132+
allHtmlFiles.forEach(file => {
133+
let content = fs.readFileSync(file, 'utf8');
134+
let originalContent = content;
135+
136+
// Custom parsing opening tags <a>
137+
let newContent = content.replace(/<a\s+([^>]+)>/gi, (match, innerProps) => {
138+
let lowerProps = innerProps.toLowerCase();
139+
140+
// Check if it's an amazon link
141+
if (!lowerProps.includes('amazon.com') && !lowerProps.includes('amzn.to')) {
142+
return match;
143+
}
144+
145+
// Extract href
146+
let hrefMatch = innerProps.match(/href=["']([^"']+)["']/i);
147+
if (!hrefMatch) return match;
148+
let oldHref = hrefMatch[1];
149+
let newHref = oldHref;
150+
151+
// Modify HRREF
152+
if (!newHref.toLowerCase().includes('tag=techstackglob-20')) {
153+
if (newHref.includes('?')) {
154+
newHref = `${newHref}&tag=techstackglob-20`;
155+
} else {
156+
newHref = `${newHref}?tag=techstackglob-20`;
157+
}
158+
}
159+
160+
// Extract target
161+
let targetMatch = innerProps.match(/target=["']([^"']*)["']/i);
162+
let oldTarget = targetMatch ? targetMatch[1] : '';
163+
let newTarget = '_blank';
164+
165+
// Extract rel
166+
let relMatch = innerProps.match(/rel=["']([^"']*)["']/i);
167+
let oldRel = relMatch ? relMatch[1] : '';
168+
let relTokens = oldRel ? oldRel.split(' ').filter(x => x) : [];
169+
170+
let requiredTokens = ['nofollow', 'noopener', 'sponsored'];
171+
requiredTokens.forEach(token => {
172+
if (!relTokens.map(t => t.toLowerCase()).includes(token)) {
173+
relTokens.push(token);
174+
}
175+
});
176+
let newRel = relTokens.join(' ');
177+
178+
// Reconstruct
179+
let remaining = innerProps
180+
.replace(/href=["'][^"']+["']/gi, '')
181+
.replace(/target=["'][^"']*["']/gi, '')
182+
.replace(/rel=["'][^"']*["']/gi, '');
183+
184+
// create clean tag
185+
let newTag = `<a ${remaining.trim()} href="${newHref}" target="${newTarget}" rel="${newRel}">`.replace(/\s+/g, ' ').replace(' >', '>');
186+
187+
csvContent += `"${file}","${oldHref}","${newHref}","${oldRel}","${newRel}","${oldTarget}","${newTarget}"\n`;
188+
189+
return newTag;
190+
});
191+
192+
if (newContent !== originalContent) {
193+
fs.writeFileSync(file, newContent);
194+
console.log(`[Affiliate] Fixed in ${file}`);
195+
}
196+
});
197+
198+
fs.writeFileSync('affiliate_report.csv', csvContent);
199+
200+
// 5. Mobile Overflow
201+
mobileRiskFiles.forEach(file => {
202+
let p = file;
203+
if (fs.existsSync(p)) {
204+
let content = fs.readFileSync(p, 'utf8');
205+
let oContent = content;
206+
207+
// Remove existing table-responsive div to rebuild them carefully
208+
content = content.replace(/<div class=["']table-responsive["']>\s*(<table[\s\S]*?<\/table>)\s*<\/div>/gi, '$1');
209+
content = content.replace(/(<table[\s\S]*?<\/table>)/gi, '<div class="table-responsive">\n$1\n</div>');
210+
211+
// Images: max-width: 100%, height: auto, loading="lazy"
212+
// Also remove inline width="..." height="..." to let CSS logic handle it
213+
content = content.replace(/<img[^>]+>/gi, (match) => {
214+
let newImg = match;
215+
if (!newImg.includes('max-width: 100%') && !newImg.includes('max-width:100%')) {
216+
if (!newImg.includes('style=')) {
217+
newImg = newImg.replace(/<img/i, '<img style="max-width: 100%; height: auto;"');
218+
} else {
219+
newImg = newImg.replace(/style=["']([^"']*)["']/i, 'style="max-width: 100%; height: auto; $1"');
220+
}
221+
}
222+
if (!newImg.includes('loading=')) newImg = newImg.replace(/<img/i, '<img loading="lazy"');
223+
return newImg;
224+
});
225+
226+
// Inline width fixes for extreme pixels e.g. width: 600px
227+
content = content.replace(/width:\s*[4-9][0-9]{2,}px;?/gi, 'max-width: 100%; height: auto;');
228+
229+
if (content !== oContent) {
230+
fs.writeFileSync(p, content);
231+
console.log(`[Mobile] Responsive fixes in ${p}`);
232+
}
233+
}
234+
});
235+
236+
// 6. Orphan Fix: samsung-odyssey-g8-vs-alienware-aw3423dwf.html
237+
// Attach link from best-ultrawide-monitors-2026.html
238+
let p1 = 'posts/best-ultrawide-monitors-2026.html';
239+
if (fs.existsSync(p1)) {
240+
let c1 = fs.readFileSync(p1, 'utf8');
241+
if (!c1.includes('samsung-odyssey-g8-vs-alienware-aw3423dwf.html')) {
242+
// Add inside a relevant text paragraph
243+
c1 = c1.replace(/Our full comparison of these top contenders/i, 'Our full <a href="samsung-odyssey-g8-vs-alienware-aw3423dwf.html">AW3423DWF vs Odyssey G8 comparison</a>');
244+
if (c1 === fs.readFileSync(p1, 'utf8')) {
245+
// Find another anchor
246+
c1 = c1.replace(/Alienware AW3423DWF/i, '<a href="samsung-odyssey-g8-vs-alienware-aw3423dwf.html">AW3423DWF vs Odyssey G8 comparison</a> (also featuring the Alienware AW3423DWF)');
247+
}
248+
fs.writeFileSync(p1, c1);
249+
console.log(`[Orphan] Added to ${p1}`);
250+
}
251+
}
252+
253+
let p2 = 'posts/alienware-aw3423dwf-review.html';
254+
if (fs.existsSync(p2)) {
255+
let c2 = fs.readFileSync(p2, 'utf8');
256+
if (!c2.includes('samsung-odyssey-g8-vs-alienware-aw3423dwf.html')) {
257+
c2 = c2.replace(/(<\/article>)/i, `<p>If you're still on the fence, see our <a href="samsung-odyssey-g8-vs-alienware-aw3423dwf.html">AW3423DWF vs Odyssey G8 comparison</a> for a deep-dive against its biggest rival.</p>\n$1`);
258+
fs.writeFileSync(p2, c2);
259+
console.log(`[Orphan] Added to ${p2}`);
260+
}
261+
}
262+
263+
console.log('All fixes applied successfully!');

0 commit comments

Comments
 (0)