Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
91300fd
Implement blog snippet system and new landing page
Cnnb01 Jul 25, 2025
d737f6a
Adding additional styling to the blog pages
Cnnb01 Jul 28, 2025
db70c24
Adding additional styling to the blog pages
Cnnb01 Jul 28, 2025
347fea9
Add email subscription form integration and responsive media queries
Cnnb01 Jul 30, 2025
a3a55ed
Removing initial media queries
Cnnb01 Aug 6, 2025
e5271ed
Adding media queries for blogs snippet page
Cnnb01 Aug 7, 2025
24924f5
Adding media queries for individual blogs display pages
Cnnb01 Aug 8, 2025
065b9f0
Debbugging a responsivity error
Cnnb01 Aug 12, 2025
8b863fd
format document
Ericgacoki Aug 6, 2025
f1a5592
add initial resp... mobile first
Ericgacoki Aug 14, 2025
4eae751
fill vh, adjust font and spacing
Ericgacoki Aug 14, 2025
9f5f6ab
add text over image
Ericgacoki Aug 14, 2025
3d560e9
adjust text/image for large screen
Ericgacoki Aug 14, 2025
10242ed
fix mystery gap
Ericgacoki Aug 14, 2025
f74f774
add css for larger screens keeping it DRY
Ericgacoki Aug 15, 2025
5dd1241
Keep updated landing page and new css
Cnnb01 Aug 15, 2025
6b35f55
Set up [slug].astro to render blog details dynamically
Cnnb01 Aug 18, 2025
4ca5a91
Adding utf-8 encoding to [slug].astro page
Cnnb01 Aug 19, 2025
565bf1e
Fixing a typescript error
Cnnb01 Aug 19, 2025
7b74943
Fix multiple issues in project
Cnnb01 Aug 22, 2025
1c36fad
add navigation links to next and previous blog
Ericgacoki Aug 22, 2025
447ed49
Merge branch 'landing_page' into links
Ericgacoki Aug 22, 2025
b32ab4e
Corrected styling that differed from the original wordpress site
Cnnb01 Sep 8, 2025
e36c150
add link to blog titles and fix kit form style
Ericgacoki Sep 11, 2025
a8f408a
update X logo
Ericgacoki Sep 11, 2025
ca454b8
update X logo
Ericgacoki Sep 11, 2025
0a1eee3
fix style for blogs an fix other minor issues
Ericgacoki Sep 11, 2025
92ba828
fix page titles to handle special characters
Ericgacoki Sep 11, 2025
37bb713
Merge pull request #4 from AkongaLabs/minor_fixes
Cnnb01 Sep 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion landing-panda/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// @ts-check
import { defineConfig } from 'astro/config';

import react from '@astrojs/react';

// https://astro.build/config
export default defineConfig({});
export default defineConfig({
integrations: [react()]
});
7 changes: 6 additions & 1 deletion landing-panda/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
"astro": "astro"
},
"dependencies": {
"astro": "^5.9.2"
"@astrojs/react": "^4.3.0",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"astro": "^5.9.2",
"react": "^19.1.0",
"react-dom": "^19.1.0"
}
}
Binary file added landing-panda/public/images/blog1.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added landing-panda/public/images/blog2.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added landing-panda/public/images/blog3.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added landing-panda/public/images/blog4.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions landing-panda/src/data/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
interface BlogPost {
id: number;
date: string;
slug: string;
title: {
rendered: string;
};
guid: {
rendered: string;
};
content: {
rendered: string;
};
excerpt: {
rendered: string;
};
link: string;
author: number;
jetpack_featured_media_url: string;

// embedded data
_embedded?: {
author?: {
name: string;
}[];
};
}

export type { BlogPost };
91 changes: 91 additions & 0 deletions landing-panda/src/pages/[slug].astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
import type { BlogPost } from "../data/types";
import "../../src/styles/mylanding.css";
Comment on lines +1 to +3

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Sanitize injected HTML and avoid set:html where plain text will do (XSS hardening)

Title, content and other WP-rendered fields can contain HTML. Injecting them directly with set:html is a high-impact XSS risk if upstream sanitization ever regresses or plugins add unsafe markup.

  • Use sanitize-html on the post body.
  • For titles, prefer plain text rendering instead of set:html.

Apply these diffs:

 ---
-import type { BlogPost } from "../data/types";
-import "../../src/styles/mylanding.css";
+import type { BlogPost } from "../data/types";
+import sanitizeHtml from "sanitize-html";
+import "../styles/mylanding.css";
 export async function getStaticPaths() {
-const { post } = Astro.props as { post: BlogPost };
+const { post } = Astro.props as { post: BlogPost };
+// Avoid injecting raw HTML where not necessary; sanitize rich content.
+const titleText = (post.title?.rendered ?? "").replace(/<[^>]*>/g, "");
+const contentHTML = sanitizeHtml(post.content?.rendered ?? "", {
+  allowedTags: sanitizeHtml.defaults.allowedTags.concat([
+    "img",
+    "figure",
+    "figcaption",
+    "h1",
+    "h2",
+    "h3",
+    "span",
+    "iframe",
+  ]),
+  allowedAttributes: {
+    a: ["href", "name", "target", "rel"],
+    img: ["src", "alt", "loading", "decoding", "width", "height", "srcset", "sizes"],
+    iframe: ["src", "width", "height", "allow", "allowfullscreen", "frameborder"],
+    "*": ["class", "id", "style"],
+  },
+  allowedSchemes: ["http", "https", "mailto"],
+});
-            <h1 set:html={post.title.rendered}></h1>
+            <h1>{titleText}</h1>
-            <div set:html={post.content.rendered}></div>
+            <div set:html={contentHTML}></div>

Also applies to: 14-15, 29-29, 42-42

import { preview } from "astro";
export async function getStaticPaths() {
// _embed query param expands related objects like authors
const res = await fetch("https://akongalabs.com/wp-json/wp/v2/posts?_embed");
const posts: BlogPost[] = await res.json();

return posts.map((post, index) => ({
params: { slug: post.slug },
props: {
post,
prevPost: posts[index + 1] || null,
nextPost: posts[index - 1] || null,
},
}));
}

const { post, prevPost, nextPost } = Astro.props as {
post: BlogPost;
prevPost: BlogPost | null;
nextPost: BlogPost | null;
};
---

<html>
<head>
<title set:html={post.title.rendered}></title>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1"/>
<meta name="generator" content={Astro.generator} />

</head>
<body>
<article class="blog-post">
<div class="blog-body">
<div class="first_links_snips">
<a href="/newLanding">Akong'a Labs</a>
<a href="/blogSnippets">Blogs</a>
</div>
<div class="blog-content">
<h1 set:html={post.title.rendered} />
<img src={post.jetpack_featured_media_url} />
<p class="author-date">
<span class="author">{post._embedded?.author?.[0]?.name}</span>
Comment on lines +44 to +47

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Render a safe title and harden featured image (a11y + perf)

  • Avoid set:html for the title; use plain text.
  • Add alt text and lazy/async hints; render image only if URL exists.
-          <h1 set:html={post.title.rendered} />
-          <img src={post.jetpack_featured_media_url} />
+          <h1>{titleText}</h1>
+          {
+            post.jetpack_featured_media_url && (
+              <img
+                src={post.jetpack_featured_media_url}
+                alt={titleText}
+                loading="lazy"
+                decoding="async"
+              />
+            )
+          }

Consider adding width/height to reduce CLS if dimensions are known.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<h1 set:html={post.title.rendered} />
<img src={post.jetpack_featured_media_url} />
<p class="author-date">
<span class="author">{post._embedded?.author?.[0]?.name}</span>
<h1>{titleText}</h1>
{post.jetpack_featured_media_url && (
<img
src={post.jetpack_featured_media_url}
alt={titleText}
loading="lazy"
decoding="async"
/>
)}
<p class="author-date">
<span class="author">{post._embedded?.author?.[0]?.name}</span>
🤖 Prompt for AI Agents
In landing-panda/src/pages/[slug].astro around lines 40-43, replace the unsafe
set:html title with a plain-text title binding (so the title is escaped and not
injected as HTML); render the featured image only when
post.jetpack_featured_media_url exists; add an alt attribute (preferably from
post._embedded?.author? or post.jetpack_featured_media_caption, falling back to
post.title.rendered or an empty string), and add loading="lazy" and
decoding="async" attributes for a11y and perf; if image width/height metadata is
available (e.g. post.jetpack_featured_media_width/height or similar), include
those attributes to reduce CLS.

<span class="date">
{
new Date(post.date).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})
}
</span>
</p>

<div set:html={post.content.rendered} />
</div>
<script async data-uid="4e5ba9cc5c" src="https://akongalabs.kit.com/4e5ba9cc5c/index.js"></script>
<hr />

{
(prevPost || nextPost) && (
<nav class="blog-navigation">
<div class="nav-links">
{prevPost && (
<a href={`/${prevPost.slug}`}>
Previous: <span set:html={prevPost.title.rendered} />
</a>
)}

{nextPost && (
<a href={`/${nextPost.slug}`}>
Next: <span set:html={nextPost.title.rendered} />
</a>
)}
</div>
</nav>
)
}

<div class="last_links">
<p><a href="https://x.com/adrianmurage">𝕏</a></p>
<p><a href="https://github.com/AkongaLabs">GitHub</a></p>
</div>
</div>
</article>
</body>
</html>
55 changes: 55 additions & 0 deletions landing-panda/src/pages/blogSnippets.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
import "../../src/styles/mylanding.css";
import type { BlogPost } from "../data/types";

const res = await fetch("https://akongalabs.com/wp-json/wp/v2/posts");
const posts: BlogPost[] = await res.json();
---
Comment on lines +1 to +7

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Sanitize HTML, avoid unnecessary set:html, and improve heading semantics

  • Excerpt is injected as HTML; sanitize it.
  • Date doesn’t need set:html; render as text.
  • Prefer H2 for each post card to keep a single H1 per page.

Apply these diffs:

 ---
-import "../../src/styles/mylanding.css";
+import "../styles/mylanding.css";
 import type { BlogPost } from "../data/types";
+import sanitizeHtml from "sanitize-html";
 
-const res = await fetch("https://akongalabs.com/wp-json/wp/v2/posts");
-const posts: BlogPost[] = await res.json();
+const res = await fetch("https://akongalabs.com/wp-json/wp/v2/posts?per_page=10");
+let posts: BlogPost[] = [];
+if (res.ok) {
+  posts = (await res.json()) as BlogPost[];
+} else {
+  console.warn("Failed to fetch posts:", res.status, res.statusText);
+}
 ---
-              <p class="bluecolor" set:html={formattedDate}></p>
-              <h1 class="blog-title" set:html={post.title.rendered}></h1>
-              <div class="para_space" set:html={cleanedExcerpt}></div>
+              <p class="bluecolor">{formattedDate}</p>
+              <h2 class="blog-title">
+                {post.title.rendered.replace(/<[^>]*>/g, "")}
+              </h2>
+              <div
+                class="para_space"
+                set:html={sanitizeHtml(cleanedExcerpt, {
+                  allowedTags: ["p", "em", "strong", "a", "code", "pre", "ul", "ol", "li", "span", "br"],
+                  allowedAttributes: { a: ["href", "rel", "target"] },
+                  allowedSchemes: ["http", "https", "mailto"],
+                })}
+              ></div>

Also applies to: 34-38

🤖 Prompt for AI Agents
In landing-panda/src/pages/blogSnippets.astro lines 1-7 (and also apply same
fixes to lines 34-38), the excerpt is being injected unsanitized with set:html,
the post date is rendered using set:html unnecessarily, and headings use H1 per
post; fix by sanitizing the excerpt HTML before rendering (use a sanitizer
library or a safe-html utility and pass the sanitized HTML to set:html), stop
using set:html for the date and render it as plain text (e.g., interpolate the
formatted date string), and change per-post headings from H1 to H2 to preserve a
single page H1; ensure imports include the sanitizer and update markup
accordingly.


<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<title>Blog snippets</title>
</head>
<body>
<div class="blogs_container">
<div class="first_links">
<a href="/newLanding">Akong'a Labs</a>
<a href="/blogSnippets">Blogs</a>
</div>

<div>
{posts.map((post) => {
const formattedDate = new Date(post.date).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
});
const cleanedExcerpt = post.excerpt.rendered.replace(/\[&hellip;\]/g, "...");

return (
<div class="asnip">
<p class="bluecolor" set:html={formattedDate}></p>
<h1 class="blog-title">
<a href={`/${post.slug}`} set:html={post.title.rendered}></a>
</h1>
<div class="para_space" set:html={cleanedExcerpt}></div>
<a class="readmore" href={`/${post.slug}`}>Read more →</a>
</div>
);
})}
</div>

<p class="insider">Get insider posts straight in your inbox</p>

<script async data-uid="4e5ba9cc5c" src="https://akongalabs.kit.com/4e5ba9cc5c/index.js"></script>
<div class="last_links">
<p><a href="https://x.com/adrianmurage">𝕏</a></p>
<p><a href="https://github.com/AkongaLabs">GitHub</a></p>
</div>
</div>
</body>
</html>
<!-- <script async data-uid="4e5ba9cc5c" src="https://akongalabs.kit.com/4e5ba9cc5c/index.js"></script> -->
3 changes: 2 additions & 1 deletion landing-panda/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const baseUrl = isDev ? "http://localhost:3001" : "";
<body>
<h1>🐼 Hello from Astro static site landing page</h1>
<p>Welcome to Landing Panda!</p>
<p><a href="/landing">Go to Landing Page</a></p>
<!-- <p><a href="/landing">Go to Landing Page</a></p> -->
<p><a href="/newLanding">Go to Landing Page</a></p>
<p><a href={`${baseUrl}/app`}>Go to the React App</a></p>
<p><a href={`${baseUrl}/api/health`}>Check API Health</a></p>
</body>
Expand Down
53 changes: 53 additions & 0 deletions landing-panda/src/pages/newLanding.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
// import "../../public/styles/mylanding.css";
import "../../src/styles/mylanding.css"
const isDev = import.meta.env.DEV;
const baseUrl = isDev ? "http://localhost:3001" : "";
Comment on lines +4 to +5

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove unused baseUrl variable.

The baseUrl variable is declared but never used in the component, creating unnecessary code.

Apply this diff to remove the unused variable:

---
import '../../public/styles/mylanding.css'
-const isDev = import.meta.env.DEV;
-const baseUrl = isDev ? "http://localhost:3001" : "";
---
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isDev = import.meta.env.DEV;
const baseUrl = isDev ? "http://localhost:3001" : "";
---
import '../../public/styles/mylanding.css'
---
🤖 Prompt for AI Agents
In landing-panda/src/pages/newLanding.astro at lines 3 to 4, the variable
baseUrl is declared but never used. Remove the declaration of baseUrl entirely
to clean up the code and avoid unused variables.

---

<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1"
/>
<meta name="generator" content={Astro.generator} />
<title>Akong’a Labs</title>
</head>
<body>
<div class="container">
<div class="content">
<div class="first_links">
<a href="/newLanding">Akong'a Labs</a>
<a href="/blogSnippets">Blogs</a>
</div>
<div class="about">
<h2 class="title">Akong’a — The number “1” in the Pokot language.</h2>
<p>
Akonga Labs is the first of a new kind of company, in Kenya, and
Africa. A <s>startup</s> <strong>stayup</strong> that builds a sustainable business first.
</p>
<p>
Prioritizing building <strong>needed products</strong> in <strong>existing</strong>
and <strong>validated markets.</strong> Prioritizing <strong>profit</strong> over “growth at all cost”.
</p>
</div>

<div class="last_links">
<p><a href="https://x.com/adrianmurage">𝕏</a></p>
<p><a href="https://github.com/AkongaLabs">GitHub</a></p>
</div>
</div>
<div class="image_container">
<img
class="header_image"
src="/images/header-background.webp"
alt="header image"
/>
<div class="image_text">Akong'a Labs</div>
</div>
</div>
</body>
</html>
Loading
Loading