Skip to content

Latest commit

 

History

History
416 lines (304 loc) · 9.83 KB

github projects plan.md

File metadata and controls

416 lines (304 loc) · 9.83 KB

Yes. For the design we mocked up, I’d keep the component architecture fairly small and data-driven. Since Astro renders components to static HTML by default, you can build almost all of this as .astro components and only add client-side JS for things like tabs/filtering. Astro Docs

I’d structure it roughly like this:

src/
├── pages/
│   └── resources.astro
│
├── components/
│   └── resources/
│       ├── ResourcesHero.astro
│       ├── SectionHeader.astro
│       │
│       ├── GitHubProjects.astro
│       ├── GitHubRepoCard.astro
│       │
│       ├── TopLists.astro
│       ├── TopListTabs.astro
│       ├── TopListPanel.astro
│       ├── RankedTool.astro
│       │
│       └── ToolIcon.astro
│
└── data/
    ├── repositories.ts
    └── resources.ts

The important components

ResourcesHero.astro

This is the top portion from the mockup:

>_ RESOURCES

Tools, Projects
and Recommendations

A collection of my open source projects and
the tools I actually use...

[Open Source] [Curated Lists] [Real World Use] [Continuously Updated]

I would not make the illustration part of the component itself. Treat that as an image/background asset so you can swap it later.


GitHubProjects.astro

This owns the entire:

GITHUB REPOSITORIES

My Open Source Projects
Tools, scripts, and projects I build and use.

                                      [View All on GitHub]

[ repo ] [ repo ] [ repo ] [ repo ]

Its job is basically:

{repositories.map((repo) => (
    <GitHubRepoCard repo={repo} />
))}

GitHubRepoCard.astro

This is the component I'd spend the most time polishing.

Each card should understand:

interface Repo {
    owner: string;
    name: string;
    description: string;
    url: string;
    language: string;
    stars?: number;
    forks?: number;
    icon?: string;
    featured?: boolean;
}

Then you can use it anywhere:

<GitHubRepoCard repo={repo} />

Visually:

┌───────────────────────────────┐
│ [icon]  codeholics/       ★   │
│         homelab-scripts       │
│                               │
│ Automation scripts for a      │
│ self-hosted homelab setup.    │
│                               │
│ ● Bash       ☆ 97    ⑂ 18     │
└───────────────────────────────┘

I would give these a very subtle hover effect:

border-gray
      ↓
border-cyan
box-shadow: cyan glow
translate-y: -2px

Nothing excessive.


The Top 10 system

This is where I would make the architecture reusable rather than creating a separate component for every list.

You might eventually have:

AI Tools
Docker & Homelab
Closed Source Replacements
Developer Tools
Linux Tools
Privacy Tools
Self-Hosted Apps
Networking Tools
Security Tools
Productivity

So don't create:

AITools.astro
DockerTools.astro
PrivacyTools.astro
...

Instead make one generic list system.

TopLists.astro

Controls the whole section.

★ TOP 10 LISTS

Curated Top 10 Lists

[ AI Tools ]
[ Docker & Homelabs ]
[ Closed Source Replacements ]
[ Developer Tools ]

┌──────────────────────────────────────────┐
│ 🤖 Top 10 AI Tools        [View Full List]
│
│ 1 ChatGPT           6 Midjourney
│ 2 Claude            7 Perplexity
│ 3 Gemini            8 Runway
│ ...
└──────────────────────────────────────────┘

TopListTabs.astro

This is one of the few places that needs browser interaction.

Click:

Docker & Homelabs

and swap the displayed list without reloading.

You don't need React for this.

Astro supports normal <script> elements for lightweight browser interactions, so I'd probably keep this vanilla TypeScript/JavaScript unless your site already has a UI framework installed. Astro Docs

On mobile I'd make these horizontally scrollable:

← AI | Docker | Replacements | Dev Tools →

rather than trying to squeeze four tabs across the screen.


TopListPanel.astro

Generic container receiving something like:

{
    slug: "ai-tools",
    title: "Top 10 AI Tools",
    description:
        "The best AI tools for productivity, development and content creation.",
    icon: "robot",
    items: [...]
}

This component shouldn't care whether it's displaying AI tools, Docker containers, Linux applications, or closed-source alternatives.


RankedTool.astro

The individual row:

┌─────────────────────────────────────────┐
│ 1  [icon] ChatGPT                    ›  │
│           Best all-around AI assistant  │
└─────────────────────────────────────────┘

Props:

interface RankedTool {
    rank: number;
    name: string;
    description: string;
    url: string;
    icon?: string;
    badge?: string;
}

Later you could support:

#1  Immich
    Self-hosted Google Photos alternative

    Docker • Open Source • Self Hosted

without changing the component.


The most important part: data

I wouldn't hardcode the actual lists into the components.

Have:

src/data/resources.ts

Something like:

export const resourceLists = [
    {
        slug: "ai-tools",
        title: "Top 10 AI Tools",
        shortTitle: "AI Tools",
        icon: "bot",
        description:
            "The AI tools I actually use and recommend.",
        items: [
            {
                rank: 1,
                name: "ChatGPT",
                description: "Best all-around AI assistant.",
                url: "...",
                icon: "..."
            },
            // ...
        ]
    },

    {
        slug: "docker-homelab",
        title: "Top 10 Docker & Homelab Tools",
        shortTitle: "Docker & Homelabs",
        icon: "container",
        items: [...]
    },

    {
        slug: "closed-source-replacements",
        title: "Top 10 Closed Source Replacements",
        shortTitle: "Open Source Alternatives",
        icon: "lock-open",
        items: [...]
    }
];

Then your page becomes surprisingly simple:

---
import Layout from "../layouts/Layout.astro";

import ResourcesHero from "../components/resources/ResourcesHero.astro";
import GitHubProjects from "../components/resources/GitHubProjects.astro";
import TopLists from "../components/resources/TopLists.astro";

import { repositories } from "../data/repositories";
import { resourceLists } from "../data/resources";
---

<Layout title="Resources | Codeholics">

    <ResourcesHero />

    <GitHubProjects repositories={repositories} />

    <TopLists lists={resourceLists} />

</Layout>

That's basically the entire page.

Astro is particularly good for this approach because component frontmatter can import data and generate the final static markup at build time, so you're not introducing a client-side app just to render cards. Astro Docs

One thing I'd change from my mockup

Since you're removing the right sidebar/category/tag bento boxes, I'd take advantage of that.

I would make /resources a true full-width page.

Your normal articles can remain:

       Article content     Sidebar
       ───────────────     ───────

while Resources gets:

┌──────────────────────────────────────────────┐
│ HERO                                         │
├──────────────────────────────────────────────┤
│       4 × GITHUB PROJECT GRID                │
├──────────────────────────────────────────────┤
│                                              │
│             TOP 10 CONTENT                   │
│                                              │
└──────────────────────────────────────────────┘

At desktop widths, I'd target roughly max-w-7xl / 1280px. That gives those four repo cards enough room without making the page feel stretched.

One additional component I'd seriously consider

I'd add:

ResourceBadge.astro

A tiny reusable badge for things like:

OPEN SOURCE
SELF HOSTED
FREE
PAID
FREEMIUM
LINUX
DOCKER
PRIVACY
MY PICK

Then eventually your entries could look like:

#1  Immich

Self-hosted photo and video management.

[OPEN SOURCE] [SELF HOSTED] [DOCKER]

Why I like it →

That would fit Codeholics extremely well and, more importantly, distinguish this page from yet another generic "Top 10 Tools" page.

I would keep all of this as Astro components rather than introducing React. Astro's static-by-default component model is a very good fit here, with only the tab/filter behavior needing a little client-side JS. Astro Docs

Astro component documentation