From 97dc0d9ad63b718ee535c2e771050bec2dbac45a Mon Sep 17 00:00:00 2001
From: Kaan Kacar
Date: Fri, 12 Jun 2026 16:56:06 +0300
Subject: [PATCH 1/8] Add community skills search, pagination, and an "Add your
skill" section
- CommunitySearch client island filters the server-rendered community
cards by title or any part of the description, and paginates them at
8 per page. Cards stay in the static HTML for agents and crawlers.
- "Add your skill" block explains how to get listed via a PR against
ECOSYSTEM_CARDS in site/src/data/skills.ts, entry format inlined.
- The llms.txt community blurb now tells agents how to submit a skill.
---
site/CLAUDE.md | 15 ++-
site/scripts/generate-llms-txt.mjs | 2 +-
site/src/app/_components/CommunitySearch.tsx | 106 +++++++++++++++++++
site/src/app/page.tsx | 44 +++++++-
site/src/app/styles.scss | 78 ++++++++++++++
5 files changed, 238 insertions(+), 7 deletions(-)
create mode 100644 site/src/app/_components/CommunitySearch.tsx
diff --git a/site/CLAUDE.md b/site/CLAUDE.md
index e6e05b1..a5764e1 100644
--- a/site/CLAUDE.md
+++ b/site/CLAUDE.md
@@ -34,10 +34,13 @@ client component.
`scripts/generate-llms-txt.mjs`.
- `src/app/page.tsx`: server-rendered landing page.
- `src/app/_components/`: `SkillCard` (server), `icons` (server, inline
- SVGs), `CopyButton` / `SkillsFilter` / `ThemeSwitchIsland` (client
- islands). `SkillsFilter` is used twice — once for the skills grid
- (`All` + categories) and once for the Installing section's tabs
- (Claude Code / Cursor / npx skills / Clone repo).
+ SVGs), `CopyButton` / `SkillsFilter` / `CommunitySearch` /
+ `ThemeSwitchIsland` (client islands). `SkillsFilter` is used twice — once
+ for the skills grid (`All` + categories) and once for the Installing
+ section's tabs (Claude Code / Cursor / npx skills / Clone repo).
+ `CommunitySearch` adds search + pagination to the community grid; the
+ cards render server-side and are passed in as children, so every entry
+ stays in the static HTML.
- `scripts/copy-skills.mjs`: mirrors `../skills/` into
`public/` on `predev` / `prebuild`. No network; just `cp`.
- `scripts/generate-llms-txt.mjs`: writes `public/llms.txt` from
@@ -80,7 +83,9 @@ then append to `SKILL_CARD_SOURCES`:
`pnpm sync:skills && pnpm dev` to verify. New category? Add it to the
`FilterType` union and the `FILTERS` array.
-**Ecosystem:** external link, no upstream copy.
+**Ecosystem:** external link, no upstream copy. New entries are picked
+up automatically by the community search, pagination, and llms.txt; no
+other wiring needed.
```ts
{
diff --git a/site/scripts/generate-llms-txt.mjs b/site/scripts/generate-llms-txt.mjs
index bd7afd0..dc6b9af 100644
--- a/site/scripts/generate-llms-txt.mjs
+++ b/site/scripts/generate-llms-txt.mjs
@@ -137,7 +137,7 @@ if (realEcosystem.length > 0) {
lines.push("## Community Built");
lines.push("");
lines.push(
- "Other community-built skills that may be helpful for your build. These aren't installed via the methods above; each project has its own setup, so follow the link on each entry. Not endorsed by the Stellar Foundation; do your own research.",
+ "Other community-built skills that may be helpful for your build. These aren't installed via the methods above; each project has its own setup, so follow the link on each entry. Not endorsed by the Stellar Foundation; do your own research. To get a skill listed here, open a pull request adding it to ECOSYSTEM_CARDS in site/src/data/skills.ts of https://github.com/stellar/stellar-dev-skill.",
);
lines.push("");
for (const c of realEcosystem) {
diff --git a/site/src/app/_components/CommunitySearch.tsx b/site/src/app/_components/CommunitySearch.tsx
new file mode 100644
index 0000000..f926772
--- /dev/null
+++ b/site/src/app/_components/CommunitySearch.tsx
@@ -0,0 +1,106 @@
+"use client";
+
+import { Children, ReactNode, useId, useState } from "react";
+
+import { Input, Pagination } from "@stellar/design-system";
+
+/** Cards shown per page in the 2-column community grid. */
+const PAGE_SIZE = 8;
+
+type Props = {
+ /** Lowercased "title description" haystack for each child, in the
+ * same render order as `children`. Search runs against these strings
+ * so the cards themselves stay server-rendered; this island only
+ * toggles wrapper visibility. */
+ searchTexts: readonly string[];
+ /** Pre-rendered card markup (server). One child per searchTexts
+ * entry, same order. */
+ children: ReactNode;
+};
+
+/**
+ * Client island that adds search and pagination to the community
+ * skills grid. The cards render server-side and are passed in as
+ * `children` (same slot pattern as `SkillsFilter`), so every card's
+ * title/description/link stays in the static HTML for non-JS clients;
+ * this component only hides wrappers that don't match the query or
+ * fall outside the current page.
+ *
+ * Search matches a case-insensitive substring of the card title or any
+ * part of its description, so a query like "DEX" finds a card that
+ * only mentions it mid-description.
+ */
+export const CommunitySearch = ({ searchTexts, children }: Props) => {
+ const searchInputId = useId();
+ const [query, setQuery] = useState("");
+ const [page, setPage] = useState(1);
+
+ const needle = query.trim().toLowerCase();
+ const matches = searchTexts.reduce((acc, text, index) => {
+ if (text.includes(needle)) acc.push(index);
+ return acc;
+ }, []);
+
+ // Clamp instead of trusting `page`: a new query can shrink the match
+ // list below the previously selected page.
+ const pageCount = Math.max(1, Math.ceil(matches.length / PAGE_SIZE));
+ const currentPage = Math.min(page, pageCount);
+ const visible = new Set(
+ matches.slice((currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE),
+ );
+
+ return (
+ <>
+
)}
-
-
- {needle === ""
- ? ""
- : `${matches.length} community skill${
- matches.length === 1 ? "" : "s"
- } matching`}
-
>
);
};
diff --git a/site/src/app/styles.scss b/site/src/app/styles.scss
index 9b2cbfa..04fda3f 100644
--- a/site/src/app/styles.scss
+++ b/site/src/app/styles.scss
@@ -558,9 +558,15 @@ p.SkillsLanding__sectionDescription {
}
// Community search input (client island; the cards stay server-rendered).
+// Width matches one grid column (50% minus half the 16px column gap) so
+// the input lines up with the card below it.
.SkillsLanding__communitySearch {
- max-width: pxToRem(360px);
+ width: calc(50% - #{pxToRem(8px)});
margin-bottom: pxToRem(16px);
+
+ @media (max-width: 600px) {
+ width: 100%;
+ }
}
// Each grid cell wraps one server-rendered card. `display: grid` makes
@@ -583,6 +589,15 @@ p.SkillsLanding__sectionDescription {
margin: pxToRem(8px) 0 0 0;
}
+// "Showing x of y skills" line under the community grid.
+.SkillsLanding__communityCount {
+ font-family: "Inter", sans-serif;
+ font-size: pxToRem(12px);
+ line-height: pxToRem(18px);
+ color: var(--sds-clr-gray-11);
+ margin: pxToRem(12px) 0 0 0;
+}
+
.SkillsLanding__communityPagination {
display: flex;
justify-content: center;
From 500e2a4d8e4fb5eb9e07e56c029bbf57e49269e0 Mon Sep 17 00:00:00 2001
From: Kaan Kacar
Date: Fri, 12 Jun 2026 19:01:46 +0300
Subject: [PATCH 4/8] Size the community count line like the card descriptions
---
site/src/app/styles.scss | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/site/src/app/styles.scss b/site/src/app/styles.scss
index 04fda3f..b58a408 100644
--- a/site/src/app/styles.scss
+++ b/site/src/app/styles.scss
@@ -589,11 +589,13 @@ p.SkillsLanding__sectionDescription {
margin: pxToRem(8px) 0 0 0;
}
-// "Showing x of y skills" line under the community grid.
+// "Showing x of y skills" line under the community grid. Sized to match
+// the card description text (.SkillsCard__description).
.SkillsLanding__communityCount {
font-family: "Inter", sans-serif;
- font-size: pxToRem(12px);
- line-height: pxToRem(18px);
+ font-size: pxToRem(14px);
+ font-weight: 400;
+ line-height: pxToRem(20px);
color: var(--sds-clr-gray-11);
margin: pxToRem(12px) 0 0 0;
}
From 286c31af7204f90ea99e4a6bd005047ff3bd0cd7 Mon Sep 17 00:00:00 2001
From: Kaan Kacar
Date: Mon, 22 Jun 2026 23:40:15 +0300
Subject: [PATCH 5/8] Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---
site/src/app/_components/CommunitySearch.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/site/src/app/_components/CommunitySearch.tsx b/site/src/app/_components/CommunitySearch.tsx
index ee0095e..d6fdaaa 100644
--- a/site/src/app/_components/CommunitySearch.tsx
+++ b/site/src/app/_components/CommunitySearch.tsx
@@ -69,6 +69,7 @@ export const CommunitySearch = ({ searchTexts, children }: Props) => {
{Children.map(children, (child, index) => (
From 69eafc79aae9a39e120b873f27cccf47b01fa434 Mon Sep 17 00:00:00 2001
From: Kaan Kacar
Date: Mon, 22 Jun 2026 23:49:10 +0300
Subject: [PATCH 6/8] Size the community count line as a caption, not body text
Showing it at 14px (card-description size) made the status line as
prominent as the cards themselves and inconsistent with the panel's
other captions. Drop it to 12px to match sectionDescription and
addSkillText, so it reads as a quiet status line.
---
site/src/app/styles.scss | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/site/src/app/styles.scss b/site/src/app/styles.scss
index b58a408..30c4073 100644
--- a/site/src/app/styles.scss
+++ b/site/src/app/styles.scss
@@ -589,13 +589,15 @@ p.SkillsLanding__sectionDescription {
margin: pxToRem(8px) 0 0 0;
}
-// "Showing x of y skills" line under the community grid. Sized to match
-// the card description text (.SkillsCard__description).
+// "Showing x of y skills" status line under the community grid. Sized
+// like the panel's other captions (.SkillsLanding__sectionDescription,
+// .SkillsLanding__addSkillText) rather than card body text, so it reads
+// as a quiet status line instead of content.
.SkillsLanding__communityCount {
font-family: "Inter", sans-serif;
- font-size: pxToRem(14px);
+ font-size: pxToRem(12px);
font-weight: 400;
- line-height: pxToRem(20px);
+ line-height: pxToRem(18px);
color: var(--sds-clr-gray-11);
margin: pxToRem(12px) 0 0 0;
}
From 1f0badd9ace486d2741c220f0e05aabf4932a245 Mon Sep 17 00:00:00 2001
From: Kaan Kacar
Date: Tue, 23 Jun 2026 00:00:47 +0300
Subject: [PATCH 7/8] Style the community count line at 14px semibold
Match the card-description size (14px) and the section-heading weight
(Inter semibold), so the status line is legible and clearly distinct
from the muted helper text around it.
---
site/src/app/styles.scss | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/site/src/app/styles.scss b/site/src/app/styles.scss
index 30c4073..55abb6b 100644
--- a/site/src/app/styles.scss
+++ b/site/src/app/styles.scss
@@ -589,15 +589,14 @@ p.SkillsLanding__sectionDescription {
margin: pxToRem(8px) 0 0 0;
}
-// "Showing x of y skills" status line under the community grid. Sized
-// like the panel's other captions (.SkillsLanding__sectionDescription,
-// .SkillsLanding__addSkillText) rather than card body text, so it reads
-// as a quiet status line instead of content.
+// "Showing x of y skills" status line under the community grid.
+// Sized like the card description text (14px) and weighted like the
+// "Community skills" section heading (Inter, semibold).
.SkillsLanding__communityCount {
font-family: "Inter", sans-serif;
- font-size: pxToRem(12px);
- font-weight: 400;
- line-height: pxToRem(18px);
+ font-size: pxToRem(14px);
+ font-weight: 600;
+ line-height: pxToRem(20px);
color: var(--sds-clr-gray-11);
margin: pxToRem(12px) 0 0 0;
}
From a915c3170e234fd1d0a72445f9dc98224a74ff5b Mon Sep 17 00:00:00 2001
From: Kaan Kacar
Date: Tue, 23 Jun 2026 00:07:50 +0300
Subject: [PATCH 8/8] Match the community count line color to the card titles
(gray-12)
---
site/src/app/styles.scss | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/site/src/app/styles.scss b/site/src/app/styles.scss
index 55abb6b..50a90b9 100644
--- a/site/src/app/styles.scss
+++ b/site/src/app/styles.scss
@@ -590,14 +590,14 @@ p.SkillsLanding__sectionDescription {
}
// "Showing x of y skills" status line under the community grid.
-// Sized like the card description text (14px) and weighted like the
-// "Community skills" section heading (Inter, semibold).
+// Matches the ecosystem card title (h3.SkillsCard__title): 14px Inter
+// semibold in gray-12.
.SkillsLanding__communityCount {
font-family: "Inter", sans-serif;
font-size: pxToRem(14px);
font-weight: 600;
line-height: pxToRem(20px);
- color: var(--sds-clr-gray-11);
+ color: var(--sds-clr-gray-12);
margin: pxToRem(12px) 0 0 0;
}