From 93e37a3d637bff4920aad6bf635751d8f6d68d61 Mon Sep 17 00:00:00 2001 From: rocktimsaikia Date: Wed, 27 May 2026 19:11:36 +0530 Subject: [PATCH 1/8] feat: refresh card UI and surface location/company/website meta --- README.md | 12 +++ src/index.js | 91 +++++++++++------ src/style.js | 278 +++++++++++++++++++++++++++++---------------------- src/url.js | 12 +++ 4 files changed, 245 insertions(+), 148 deletions(-) create mode 100644 src/url.js diff --git a/README.md b/README.md index 84138fe..2344f38 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,18 @@ Or skip the install and load it straight from a CDN in an HTML file: ``` +## Customization + +Override the accent color with the `--gh-accent` CSS custom property on the element: + +```css +github-card { + --gh-accent: #6366f1; +} +``` + +The card also surfaces `location`, `company`, and `website` from the GitHub profile when present. + ## License 2026 © MIT [Rocktim Saikia](https://rocktimcodes.site) diff --git a/src/index.js b/src/index.js index b2dd242..136909a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,15 @@ import escapeHtml from './escape.js'; import nFormatter from './format.js'; import widgetStyle from './style.js'; +import safeUrl from './url.js'; + +// Inline monochrome icons (Material Symbols) for the profile meta row. +const ICON_LOCATION = + ''; +const ICON_COMPANY = + ''; +const ICON_LINK = + ''; const template = document.createElement('template'); @@ -60,35 +69,61 @@ class myCard extends HTMLElement { const login = escapeHtml(user.login); const htmlUrl = escapeHtml(user.html_url); const name = escapeHtml(user.name); - const bio = escapeHtml(user.bio ?? ''); + const bioHtml = user.bio + ? `

${escapeHtml(user.bio)}

` + : ''; + + const meta = []; + if (user.location) { + meta.push( + `${ICON_LOCATION}${escapeHtml(user.location)}`, + ); + } + if (user.company) { + meta.push( + `${ICON_COMPANY}${escapeHtml(user.company)}`, + ); + } + const blog = safeUrl(user.blog); + if (blog) { + const label = escapeHtml(String(user.blog).replace(/^https?:\/\//i, '')); + meta.push( + `${ICON_LINK}${label}`, + ); + } + const metaHtml = meta.length + ? `
${meta.join('')}
` + : ''; + return ` -
-
- -
-
${name}
-

${name}

- -

${bio}

- -
-
- `; +
+
+ +
+
${name}
+

${name}

+ + ${bioHtml} + ${metaHtml} + +
+
+ `; } } diff --git a/src/style.js b/src/style.js index f9ef03e..b2a91cf 100644 --- a/src/style.js +++ b/src/style.js @@ -1,133 +1,171 @@ const widgetStyle = ` -h1, div, p { - margin: 0px; - padding: 0px; - font-family: 'system-ui' -} -a{ - text-decoration: none; - color: inherit; -} -#github-logo{ - height: 20px; - position: absolute; - top: 10px; - right: 10px; -} -.cover{ - height: 120px; - width: 100%; - background: #FF5C5C; - position: absolute; - left: 0px; - top: 0px; - border-top-left-radius: 5px; - border-top-right-radius: 5px; +*, *::before, *::after { + box-sizing: border-box; } .card { - position: relative; - display: inline-block; - background: #ffffff; - border-radius: 5px; - box-shadow: 0 12px 13px rgba(0,0,0,0.16), 0 12px 13px rgba(0,0,0,0.16); - text-align: center; - padding: 20px 50px; - margin: 5px; - padding-top: 5px; - transition: all 0.5s; + --accent: var(--gh-accent, #ff5c5c); + --bg: #ffffff; + --surface: #f6f8fa; + --title: #1f2328; + --text: #57606a; + --muted: #8b949e; + --border: rgba(31, 35, 40, 0.1); + --shadow: 0 1px 3px rgba(31, 35, 40, 0.08), 0 12px 28px rgba(31, 35, 40, 0.12); + + position: relative; + display: inline-block; + width: 320px; + padding: 0 28px 28px; + background: var(--bg); + border-radius: 16px; + box-shadow: var(--shadow); + text-align: center; + overflow: hidden; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.card:hover { + transform: translateY(-3px); + box-shadow: 0 2px 6px rgba(31, 35, 40, 0.1), 0 20px 44px rgba(31, 35, 40, 0.18); +} + +/* Dark theme just remaps the palette variables — no !important overrides. */ +.card.dark { + --bg: #16181d; + --surface: #1f242c; + --title: #e6edf3; + --text: #adbac7; + --muted: #768390; + --border: rgba(255, 255, 255, 0.1); + --shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 16px 36px rgba(0, 0, 0, 0.55); +} + +a { + text-decoration: none; + color: inherit; +} + +.cover { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 96px; + background: var(--accent); } -/* Dark Theme */ -.card.dark{ - background: #1C1D21; -} -.dark .card-title{ - color: #E4E4E4 !important; - font-weight: 500 !important; -} -.dark .card-desc{ - font-weight: 400 !important; - color: #c6c6c6 !important; -} -.dark .count{ - color: #c6c6c6 !important; - font-weight: 600 !important; -} -.dark .box-text{ - color: #797979 !important; - font-weight: 500 !important; -} -.dark .footer-box{ - background: #1D2025 !important; - box-shadow: 0px 0.2px 5px rgba(255, 255, 255, 0.15), 0px 4px 10px rgba(0, 0, 0, 0.25) !important; +#github-logo { + position: absolute; + top: 14px; + right: 14px; + height: 22px; + opacity: 0.85; + transition: opacity 0.2s ease; +} +#github-logo:hover { + opacity: 1; } +.card-img-wrapper { + position: relative; + width: 104px; + height: 104px; + margin: 44px auto 14px; +} +.card-img-wrapper img { + width: 100%; + height: 100%; + border-radius: 50%; + object-fit: cover; + border: 4px solid var(--bg); + background: var(--bg); +} +.card-title { + display: inline-block; + color: var(--title); + font-size: 21px; + font-weight: 700; + letter-spacing: -0.01em; +} + +.card-responsename { + margin-top: 2px; + color: var(--muted); + font-size: 14px; +} + +.card-desc { + margin: 12px auto 0; + max-width: 250px; + color: var(--text); + font-size: 13.5px; + line-height: 1.5; +} + +.card-meta { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + gap: 4px 14px; + margin-top: 12px; +} +.meta-item { + display: inline-flex; + align-items: center; + gap: 5px; + max-width: 100%; + color: var(--muted); + font-size: 12.5px; + line-height: 1.4; +} +.meta-item svg { + flex-shrink: 0; + width: 14px; + height: 14px; + opacity: 0.8; +} +.meta-link { + transition: color 0.2s ease; +} +.meta-link:hover { + color: var(--accent); +} -.card .fa-github { - position: absolute; - color: #646464; - font-size: 20px; - top: 10px; - right: 10px; -} -.card .card-title { - color: #434343; - margin-bottom: -8px; - font-size: 25px; - font-weight: 600; -} -.card .card-responsename { - margin-bottom: 20px; - color: #797979; -} -.card .card-desc { - font-weight: 500; - width: 250px; - margin: auto; - display: block; - color: #3c3c3c; -} -.card .card-img-wrapper { - position: relative; - height: 160px; - width: 160px; - margin: 10px auto; - margin-bottom: 20px; -} -.card .card-img-wrapper img { - height: 100%; - width: 100%; - border-radius: 50%; -} -.card .card-footer { - margin-top: 40px; -} -.card .card-footer .footer-box { - position: relative; - border-top: 2px solid #ff9b9b; - box-shadow: 0 3px 6px -1px rgb(0 0 0 / 26%), 0 2px 4px -1px rgb(0 0 0 / 6%); - border-radius: 5px; - margin: 0 auto; - padding: 10px; - display: flex; - justify-content: space-around; -} -.card .card-footer .footer-box .box-wrapper { - position: relative; -} -.card .card-footer .footer-box .box-wrapper .count { - font-family: 'consolas'; - color: #434343; - font-size: 20px; - font-weight: 600; -} -.card .card-footer .footer-box .box-wrapper .box-text { - font-size: 12px; - font-weight: 600; - color: #00000085; - letter-spacing: 0.5px; +.card-footer { + margin-top: 20px; +} +.footer-box { + display: flex; + justify-content: space-around; + align-items: center; + padding: 13px 6px; + border: 1px solid var(--border); + border-top: 2px solid var(--accent); + border-radius: 12px; + background: var(--surface); +} +.box-wrapper { + flex: 1; +} +.count { + color: var(--title); + font-size: 18px; + font-weight: 700; + font-variant-numeric: tabular-nums; + line-height: 1.2; +} +.box-text { + margin-top: 3px; + color: var(--muted); + font-size: 10.5px; + font-weight: 600; + letter-spacing: 0.4px; + text-transform: uppercase; } `; diff --git a/src/url.js b/src/url.js new file mode 100644 index 0000000..e793bac --- /dev/null +++ b/src/url.js @@ -0,0 +1,12 @@ +// Sanitize a user-supplied URL (e.g. a GitHub profile's `blog` field) before +// using it in an href: allow only http(s), assume https when no scheme is given, +// and reject anything else (javascript:, data:, …) to avoid script injection. +function safeUrl(value) { + if (!value) return ''; + const url = String(value).trim(); + if (/^https?:\/\//i.test(url)) return url; + if (/^[a-z][a-z\d+.-]*:/i.test(url)) return ''; // some other scheme → reject + return `https://${url}`; +} + +export default safeUrl; From c5ec568a8100272e002bbee190a9d426a2737661 Mon Sep 17 00:00:00 2001 From: rocktimsaikia Date: Wed, 27 May 2026 19:19:13 +0530 Subject: [PATCH 2/8] feat: shorten Repositories label to Repos --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 136909a..26694a1 100644 --- a/src/index.js +++ b/src/index.js @@ -117,7 +117,7 @@ class myCard extends HTMLElement {
${nFormatter(user.public_repos)}
-
Repositories
+
Repos
From 5cea3d095833ebfdf718c1f6d7dd92d01bb95db5 Mon Sep 17 00:00:00 2001 From: rocktimsaikia Date: Wed, 27 May 2026 19:25:21 +0530 Subject: [PATCH 3/8] fix: constrain h1 font-size to remove phantom gap around the name --- src/style.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/style.js b/src/style.js index b2a91cf..ebe88ec 100644 --- a/src/style.js +++ b/src/style.js @@ -84,6 +84,11 @@ a { background: var(--bg); } +h1 { + font-size: 21px; + line-height: 1.2; +} + .card-title { display: inline-block; color: var(--title); From b5fe2c6b42c908a285840a39a377e57a28343b65 Mon Sep 17 00:00:00 2001 From: rocktimsaikia Date: Wed, 27 May 2026 19:26:21 +0530 Subject: [PATCH 4/8] style: tighten avatar margin and card bottom padding --- src/style.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/style.js b/src/style.js index ebe88ec..886dd47 100644 --- a/src/style.js +++ b/src/style.js @@ -16,7 +16,7 @@ const widgetStyle = ` position: relative; display: inline-block; width: 320px; - padding: 0 28px 28px; + padding: 0 28px 20px; background: var(--bg); border-radius: 16px; box-shadow: var(--shadow); @@ -73,7 +73,7 @@ a { position: relative; width: 104px; height: 104px; - margin: 44px auto 14px; + margin: 44px auto 8px; } .card-img-wrapper img { width: 100%; From eb39782d3e711296db32647f145f7ba9e88299ff Mon Sep 17 00:00:00 2001 From: rocktimsaikia Date: Wed, 27 May 2026 19:31:14 +0530 Subject: [PATCH 5/8] docs(examples): demo custom --gh-accent accent override --- examples/index.html | 61 +++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/examples/index.html b/examples/index.html index 552e7ed..aeb1e3c 100644 --- a/examples/index.html +++ b/examples/index.html @@ -2,31 +2,54 @@ - Github-card - - + + +

Default

- - + +
+ +

Custom accent — set --gh-accent on the element

+
+ +
+ - From 98cc41553cf18f5afff19eec895749e2832563a7 Mon Sep 17 00:00:00 2001 From: rocktimsaikia Date: Wed, 27 May 2026 19:53:08 +0530 Subject: [PATCH 6/8] docs(examples): label each demo card with the attributes it uses --- examples/index.html | 110 +++++++++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 28 deletions(-) diff --git a/examples/index.html b/examples/index.html index aeb1e3c..a3d7103 100644 --- a/examples/index.html +++ b/examples/index.html @@ -2,52 +2,106 @@ - github-card + github-card — demo -

Default

-
- - -
+

github-card

+

+ Each example below shows the exact attributes the card is using. +

+ +
+
+ +
+ Default · light theme + data-user="rocktimsaikia" +
+
+ +
+ +
+ Dark theme + data-theme="dark" +
+
+ +
+ +
+ Custom accent + style="--gh-accent: #6366f1" +
+
-

Custom accent — set --gh-accent on the element

-
- - +
+ +
+ Dark + custom accent + data-theme="dark" + --gh-accent: #22c55e +
+
From 684375b53e1604867f270557003880dad3031930 Mon Sep 17 00:00:00 2001 From: rocktimsaikia Date: Wed, 27 May 2026 20:09:03 +0530 Subject: [PATCH 7/8] docs: point demo links to the GitHub Pages demo instead of CodePen --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2344f38..d8f6162 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@

github-card 🌈

Beautiful embeddable GitHub profile card for your portfolio.
- Here is a live codepen Demo

+ Here is a live demo

CI npm version

-

+

From d9ee35eff65d85b65056ff4a0603ac068f5241ec Mon Sep 17 00:00:00 2001 From: rocktimsaikia Date: Wed, 27 May 2026 20:17:51 +0530 Subject: [PATCH 8/8] docs: show the GitHub Pages link directly in the header --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8f6162..de4394a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@

github-card 🌈

Beautiful embeddable GitHub profile card for your portfolio.
- Here is a live demo

+ https://rocktimsaikia.github.io/github-card/

CI npm version