From 9577bc9c1c94b01ab7c93f57137fe2bd5a9a3e02 Mon Sep 17 00:00:00 2001
From: Dinesh <13635627+HumbleBee14@users.noreply.github.com>
Date: Sun, 12 Jul 2026 16:29:58 -0700
Subject: [PATCH 1/4] Extract shared IconLinkCard; add .card/.card--interactive
primitives
About and Contact rendered a byte-identical icon+label+action link card
with ~100 lines of duplicated CSS each. Replace both with a single
IconLinkCard.astro, and add global .card / .card--interactive utilities
(surface + hover-lift) so the recipe lives in one place. Verified both
pages render identically in light and dark.
Also point the homepage's middle CTA at /write ('Write an article')
instead of the redundant style-guide anchor.
---
src/components/IconLinkCard.astro | 125 ++++++++++++++++++++++++++++
src/pages/about.astro | 130 +-----------------------------
src/pages/contact.astro | 128 +----------------------------
src/pages/index.astro | 2 +-
src/styles/global.css | 23 ++++++
5 files changed, 153 insertions(+), 255 deletions(-)
create mode 100644 src/components/IconLinkCard.astro
diff --git a/src/components/IconLinkCard.astro b/src/components/IconLinkCard.astro
new file mode 100644
index 0000000..10c23e1
--- /dev/null
+++ b/src/components/IconLinkCard.astro
@@ -0,0 +1,125 @@
+---
+// Icon + label + action link card. Shared by About and Contact.
+// Surface + hover-lift come from the global .card / .card--interactive classes.
+interface Props {
+ href?: string | null;
+ /** SVG path `d` for a 24×24 viewBox icon */
+ icon: string;
+ label: string;
+ action: string;
+}
+const { href, icon, label, action } = Astro.props;
+const external = !!href && href.startsWith('http');
+---
+
+
+
+
+
+ {label}
+ {action}
+ →
+
+
+
diff --git a/src/pages/about.astro b/src/pages/about.astro
index ef3017a..d6be806 100644
--- a/src/pages/about.astro
+++ b/src/pages/about.astro
@@ -1,5 +1,6 @@
---
import BaseLayout from '@/layouts/BaseLayout.astro';
+import IconLinkCard from '@/components/IconLinkCard.astro';
import { SITE } from '@/lib/site';
const GITHUB_URL = 'https://github.com/HumbleBee14';
@@ -85,23 +86,7 @@ const links = [
@@ -205,122 +190,11 @@ const links = [
grid-template-columns: repeat(2, 1fr);
gap: 14px;
}
- .about-card {
- position: relative;
- display: grid;
- grid-template-columns: 56px 1fr 28px;
- grid-template-areas:
- 'icon label arrow'
- 'icon action arrow';
- column-gap: 16px;
- row-gap: 2px;
- align-items: center;
- padding: 22px;
- border: 1px solid var(--line);
- border-radius: var(--radius-lg);
- background: var(--paper-2);
- color: inherit !important;
- text-decoration: none;
- overflow: hidden;
- transition:
- transform 0.18s ease,
- border-color 0.18s ease,
- box-shadow 0.18s ease,
- background 0.18s ease;
- }
- .about-card {
- border-bottom: 1px solid var(--line);
- }
- .about-card::before {
- content: '';
- position: absolute;
- inset: 0 auto 0 0;
- width: 3px;
- background: var(--accent);
- transform: scaleY(0);
- transform-origin: top;
- transition: transform 0.22s ease;
- }
- .about-card:hover {
- transform: translateY(-2px);
- border-color: var(--accent);
- background: var(--paper);
- box-shadow: 0 8px 24px -14px color-mix(in srgb, var(--accent) 40%, transparent);
- }
- .about-card:hover::before {
- transform: scaleY(1);
- }
- .about-card:hover .about-action,
- .about-card:hover .about-arrow,
- .about-card:hover .about-icon {
- color: var(--accent);
- }
- .about-card:hover .about-icon {
- border-color: var(--accent);
- }
- .about-card:hover .about-arrow {
- transform: translateX(4px);
- }
-
- .about-icon {
- grid-area: icon;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: 48px;
- height: 48px;
- border-radius: 10px;
- background: var(--paper);
- border: 1px solid var(--line);
- color: var(--ink-2);
- transition:
- color 0.15s ease,
- border-color 0.15s ease;
- }
- .about-icon svg {
- display: block;
- fill: currentColor;
- }
- .about-label {
- grid-area: label;
- font-family: var(--font-mono);
- font-size: 11px;
- text-transform: uppercase;
- letter-spacing: 0.1em;
- color: var(--ink-3);
- align-self: end;
- }
- .about-action {
- grid-area: action;
- font-family: var(--font-display);
- font-size: 20px;
- color: var(--ink);
- line-height: 1.15;
- word-break: break-word;
- align-self: start;
- transition: color 0.15s ease;
- }
- .about-arrow {
- grid-area: arrow;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- font-family: var(--font-mono);
- font-size: 18px;
- color: var(--ink-3);
- transition:
- transform 0.18s ease,
- color 0.15s ease;
- }
-
@media (max-width: 620px) {
.about-links {
grid-template-columns: 1fr;
gap: 10px;
}
- .about-card {
- padding: 18px;
- }
.about-body {
font-size: 16px;
}
diff --git a/src/pages/contact.astro b/src/pages/contact.astro
index 8b9cef9..0f74d2f 100644
--- a/src/pages/contact.astro
+++ b/src/pages/contact.astro
@@ -1,5 +1,6 @@
---
import BaseLayout from '@/layouts/BaseLayout.astro';
+import IconLinkCard from '@/components/IconLinkCard.astro';
import { SITE, SOCIALS } from '@/lib/site';
const channels = [
@@ -56,23 +57,7 @@ const channels = [
@@ -109,112 +94,6 @@ const channels = [
gap: 14px;
}
- .contact-card {
- position: relative;
- display: grid;
- grid-template-columns: 56px 1fr 28px;
- grid-template-areas:
- 'icon label arrow'
- 'icon action arrow';
- column-gap: 16px;
- row-gap: 2px;
- align-items: center;
- padding: 22px 22px;
- border: 1px solid var(--line);
- border-radius: var(--radius-lg);
- background: var(--paper-2);
- color: inherit;
- text-decoration: none;
- overflow: hidden;
- transition:
- transform 0.18s ease,
- border-color 0.18s ease,
- box-shadow 0.18s ease,
- background 0.18s ease;
- }
- .contact-card::before {
- content: '';
- position: absolute;
- inset: 0 auto 0 0;
- width: 3px;
- background: var(--accent);
- transform: scaleY(0);
- transform-origin: top;
- transition: transform 0.22s ease;
- }
- .contact-card:hover {
- transform: translateY(-2px);
- border-color: var(--accent);
- background: var(--paper);
- box-shadow: 0 8px 24px -14px color-mix(in srgb, var(--accent) 40%, transparent);
- }
- .contact-card:hover::before {
- transform: scaleY(1);
- }
- .contact-card:hover .contact-action {
- color: var(--accent);
- }
- .contact-card:hover .contact-arrow {
- color: var(--accent);
- transform: translateX(4px);
- }
- .contact-card:hover .contact-icon {
- color: var(--accent);
- border-color: var(--accent);
- }
-
- .contact-icon {
- grid-area: icon;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: 48px;
- height: 48px;
- border-radius: 10px;
- background: var(--paper);
- border: 1px solid var(--line);
- color: var(--ink-2);
- transition:
- color 0.15s ease,
- border-color 0.15s ease;
- }
- .contact-icon svg {
- display: block;
- fill: currentColor;
- }
-
- .contact-label {
- grid-area: label;
- font-family: var(--font-mono);
- font-size: 11px;
- text-transform: uppercase;
- letter-spacing: 0.1em;
- color: var(--ink-3);
- align-self: end;
- }
- .contact-action {
- grid-area: action;
- font-family: var(--font-display);
- font-size: 20px;
- color: var(--ink);
- line-height: 1.15;
- transition: color 0.15s ease;
- word-break: break-word;
- align-self: start;
- }
- .contact-arrow {
- grid-area: arrow;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- font-family: var(--font-mono);
- font-size: 18px;
- color: var(--ink-3);
- transition:
- transform 0.18s ease,
- color 0.15s ease;
- }
-
@media (max-width: 1000px) {
.contact-grid {
grid-template-columns: repeat(2, 1fr);
@@ -225,8 +104,5 @@ const channels = [
grid-template-columns: 1fr;
gap: 10px;
}
- .contact-card {
- padding: 18px 18px;
- }
}
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 1e4d854..1d793c8 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -307,7 +307,7 @@ const featuredTools = allTools
diff --git a/src/styles/global.css b/src/styles/global.css
index 3a48d69..2925def 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -725,6 +725,29 @@ main {
grid-template-columns: repeat(3, 1fr);
gap: var(--row-gap);
}
+/* Shared card surface + hover lift. Used by IconLinkCard and the page cards
+ (contributor, tool, community). Distinct layouts stay per-page; only the
+ surface + hover recipe live here. */
+.card {
+ border: 1px solid var(--line);
+ background: var(--paper-2);
+ color: inherit;
+ text-decoration: none;
+}
+.card--interactive {
+ transition:
+ transform 0.18s ease,
+ border-color 0.18s ease,
+ background 0.18s ease,
+ box-shadow 0.18s ease;
+}
+.card--interactive:hover {
+ transform: translateY(-2px);
+ border-color: var(--accent);
+ background: var(--paper);
+ box-shadow: var(--shadow-hover);
+}
+
.article-card {
display: flex;
flex-direction: column;
From b133d4d473cc84a8c47c39d59c1bd700c77c8a4d Mon Sep 17 00:00:00 2001
From: Dinesh <13635627+HumbleBee14@users.noreply.github.com>
Date: Sun, 12 Jul 2026 16:33:24 -0700
Subject: [PATCH 2/4] Adopt shared .card/.card--interactive on the remaining
page cards
contributor-card (authors), tool-card (playground), community-channel
(community) all repeated the same surface + hover-lift recipe. Point them
at the shared .card/.card--interactive classes and keep only their
distinct layout rules (grid/flex, padding, radius, accent bar). One hover
recipe now drives every card. Also correct the stale MIT/CC license line
in the About copy.
---
src/pages/about.astro | 5 ++---
src/pages/authors/index.astro | 17 +----------------
src/pages/community/index.astro | 22 ++++++----------------
src/pages/playground/index.astro | 19 ++-----------------
4 files changed, 11 insertions(+), 52 deletions(-)
diff --git a/src/pages/about.astro b/src/pages/about.astro
index d6be806..e44f131 100644
--- a/src/pages/about.astro
+++ b/src/pages/about.astro
@@ -78,9 +78,8 @@ const links = [
- This site is open source, MIT-licensed for the code, and Creative Commons / All Rights
- Reserved per author for the writing. No paywalls, no popups, no "10x your ML career"
- funnels. Just notes from the workbench.
+ Everything here is © its respective author, all rights reserved. No paywalls, no popups, no
+ "10x your ML career" funnels. Just notes from the workbench.
diff --git a/src/pages/authors/index.astro b/src/pages/authors/index.astro
index 10eab9d..e4c5027 100644
--- a/src/pages/authors/index.astro
+++ b/src/pages/authors/index.astro
@@ -50,7 +50,7 @@ const contributors = authors