Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions .github/og-card/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# The social card

`card.html` is the source of `og-image-2.jpg`, the 1200×630 card that LinkedIn, WhatsApp and X show for https://roanny.github.io/. It is a self-contained page at exactly 1200×630: the site's palette and logo written in place, and the three typefaces loaded from Google Fonts with `display=block`, so the screenshot waits for them. A new card is an edit of this file and a re-render, not a rebuild.

## Rendering

From the root of the checkout, with Google Chrome and ImageMagick installed and network access to Google Fonts:

```bash
cd .github/og-card
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--headless=new --disable-gpu --hide-scrollbars \
--virtual-time-budget=8000 --window-size=1200,630 \
--screenshot="$PWD/card.png" "file://$PWD/card.html"
magick card.png -quality 86 -sampling-factor 4:2:0 -strip card.jpg
magick identify card.jpg # JPEG 1200x630
```

`card.png` and `card.jpg` are ignored by git; the card the site serves is the copy at the root. On 2026-10-14, Google Chrome 155.0.8059.41 and ImageMagick 7.1.2-33 on macOS rendered a `card.jpg` byte-identical to `og-image-2.jpg`. Another version of either, or a change to the fonts Google serves, can move pixels without changing the design.

## Publishing a new card

1. Edit `card.html`, render it, and look at `card.jpg`.
2. Copy it to the root under the next filename, `og-image-3.jpg`, and remove the old card: LinkedIn, WhatsApp and X cache a card by its URL, so a new card never reuses a filename.
3. Point the three references in `index.html` at it (`og:image`, `twitter:image`, the JSON-LD `image`), and update the filename in `AGENTS.md`, `README.md` and this file.
4. Once it is live, refetch it in LinkedIn's Post Inspector (https://www.linkedin.com/post-inspector/) with https://roanny.github.io/.

The card carries the operator's name and profile, which the root `README.md` § License leaves unlicensed, like the page itself.
50 changes: 50 additions & 0 deletions .github/og-card/card.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!doctype html>
<html><head><meta charset="utf-8">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;600&display=block" rel="stylesheet">
<style>
* { margin:0; padding:0; box-sizing:border-box; }
html, body { width:1200px; height:630px; overflow:hidden; }
body {
position:relative; background:#07131d; color:#eaf4f4;
font-family:"Inter", system-ui, sans-serif;
}
.glow-a { position:absolute; left:-260px; top:-300px; width:900px; height:820px; border-radius:50%;
background:radial-gradient(closest-side, rgba(47,214,195,0.20), rgba(47,214,195,0) 100%); }
.glow-b { position:absolute; right:-300px; bottom:-360px; width:900px; height:820px; border-radius:50%;
background:radial-gradient(closest-side, rgba(255,143,107,0.10), rgba(255,143,107,0) 100%); }
.dots { position:absolute; inset:0; background-image:radial-gradient(rgba(158,216,219,0.09) 1px, transparent 1px); background-size:26px 26px; }
.wrap { position:absolute; left:96px; top:110px; right:96px; }
.brand { display:flex; align-items:center; gap:14px; }
.brand svg { width:42px; height:42px; }
.brand span { font-family:"Space Grotesk"; font-weight:700; font-size:27px; letter-spacing:.02em;
background:linear-gradient(92deg,#2fd6c3,#4cc9f0); -webkit-background-clip:text; background-clip:text; color:transparent; }
.pill { margin-top:44px; display:inline-flex; align-items:center; gap:12px; padding:14px 26px; border-radius:999px;
border:1.5px solid rgba(47,214,195,0.32); background:rgba(47,214,195,0.06);
font-family:"JetBrains Mono"; font-weight:600; font-size:19px; letter-spacing:.14em; color:#2fd6c3; }
.pill i { width:10px; height:10px; border-radius:50%; background:#2fd6c3; box-shadow:0 0 12px #2fd6c3; }
h1 { margin-top:38px; font-family:"Space Grotesk"; font-weight:700; font-size:89px; line-height:1; letter-spacing:-.01em; }
h1 em { font-style:normal; background:linear-gradient(92deg,#2fd6c3,#4cc9f0); -webkit-background-clip:text; background-clip:text; color:transparent; }
.sub { margin-top:30px; font-size:29px; color:#93adbc; }
.sub b { color:#eaf4f4; font-weight:600; }
.chips { margin-top:36px; display:flex; gap:12px; }
.chips span { padding:11px 18px; border-radius:10px; border:1.5px solid rgba(158,216,219,0.22); background:rgba(255,255,255,0.025);
font-family:"JetBrains Mono"; font-size:19px; color:#eaf4f4; }
.waves { position:absolute; left:0; right:0; bottom:0; height:70px; }
</style></head>
<body>
<div class="glow-a"></div><div class="glow-b"></div><div class="dots"></div>
<div class="wrap">
<div class="brand">
<svg viewBox="0 0 48 48" fill="none"><circle cx="33" cy="14" r="7" fill="#ff8f6b"/><path d="M6 30c6-4 12-4 18 0s12 4 18 0" stroke="#2fd6c3" stroke-width="3" stroke-linecap="round"/><path d="M10 40c5-3 10-3 15 0s10 3 15 0" stroke="#4cc9f0" stroke-width="3" stroke-linecap="round"/></svg>
<span>roanny.github.io</span>
</div>
<div class="pill"><i></i>DATA ENGINEER · GOOGLE CLOUD CERTIFIED ×2</div>
<h1>Roanny <em>Lamas López</em></h1>
<p class="sub"><b>Data Engineer</b> — scalable data ecosystems on Google Cloud</p>
<div class="chips"><span>BigQuery</span><span>Looker</span><span>Airflow / Composer</span><span>ETL · DWH</span></div>
</div>
<svg class="waves" viewBox="0 0 1200 70" preserveAspectRatio="none" fill="none">
<path d="M0 30 C150 10 300 50 450 30 S750 10 900 30 S1100 50 1200 28" stroke="rgba(76,201,240,0.5)" stroke-width="3"/>
<path d="M0 50 C160 32 320 66 480 48 S800 32 960 50 S1120 64 1200 46" stroke="rgba(47,214,195,0.35)" stroke-width="2.5"/>
</svg>
</body></html>
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
.claude/settings.local.json
.claude/.cc-writes/
.claude/worktrees/
.github/og-card/card.png
.github/og-card/card.jpg
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ The operator's personal CV and portfolio, served at https://roanny.github.io/ by
- `404.html` — the themed not-found page, self-contained.
- `favicon.svg`, `favicon-32.png`, `og-image-2.jpg` (the 1200×630 social card), `robots.txt`, `sitemap.xml`; `.nojekyll` makes Pages serve the tree as it is.
- `.github/scripts/check_site.py`, `.github/htmlvalidate.json` — the site's own gates.
- `.github/og-card/` — `card.html`, the source of the social card, and the command that renders it.

## Previewing and checking

- Preview: `python3 -m http.server 8000`, then http://localhost:8000 (`?lang=en|es` and `?theme=light|dark` force the language and the theme).
- The gates, as CI runs them: `npx --yes html-validate@11.16.0 --config .github/htmlvalidate.json index.html 404.html`, `python3 .github/scripts/check_site.py i18n`, `python3 .github/scripts/check_site.py sitemap`.
- A content change updates `lastmod` in `sitemap.xml`. A changed social card takes a new filename (`og-image-<n>.jpg`) and the three references in `index.html` (`og:image`, `twitter:image`, the JSON-LD `image`), because LinkedIn, WhatsApp and X cache a card by its URL; LinkedIn's Post Inspector refetches it once it is live.
- A content change updates `lastmod` in `sitemap.xml`. A changed social card is an edit of `.github/og-card/card.html` and a re-render (its `README.md` has the command); it takes a new filename (`og-image-<n>.jpg`) and the three references in `index.html` (`og:image`, `twitter:image`, the JSON-LD `image`), because LinkedIn, WhatsApp and X cache a card by its URL; LinkedIn's Post Inspector refetches it once it is live.

CI (`Validate`, `.github/workflows/ci.yml`) refuses invalid HTML in either page, an `i18n` key missing from either language, a malformed sitemap, a changed shared block under `.claude/`, on pull requests a commit subject over 72 characters or with a trailing period, and on pull requests and pushes a commit author other than the one below, Dependabot's or GitHub's own.

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ python3 -m http.server 8000
├── .nojekyll # Pages serves the tree as it is, without Jekyll
├── AGENTS.md # Instructions for coding agents (imported by .claude/CLAUDE.md)
├── LICENSE # Apache-2.0, for the code only (see License)
└── .github/ # Validate (ci.yml), its scripts, Dependabot
└── .github/ # Validate (ci.yml), its scripts, Dependabot, og-card/ (the card's source)
```

## Editing the text
Expand All @@ -48,7 +48,7 @@ The site shares its identity with [Coabana](https://coabana.github.io/): the sam

## SEO

`index.html` carries the canonical URL, `hreflang` (`en`, `es` and `x-default` through `?lang=`), Open Graph and Twitter cards with `og-image-2.jpg`, and `Person` JSON-LD (with the Coabana affiliation). After a content change, update `lastmod` in `sitemap.xml`.
`index.html` carries the canonical URL, `hreflang` (`en`, `es` and `x-default` through `?lang=`), Open Graph and Twitter cards with `og-image-2.jpg`, and `Person` JSON-LD (with the Coabana affiliation). The card is rendered from `.github/og-card/card.html`; `.github/og-card/README.md` has the command and the steps to publish a new one. After a content change, update `lastmod` in `sitemap.xml`.

## Development

Expand Down Expand Up @@ -81,4 +81,4 @@ This site is not part of the Looker Developer Agent and names none of its produc

The code — the HTML structure, CSS and JavaScript of `index.html` and `404.html`, and everything under `.github/` and `.claude/` — is licensed under Apache-2.0; see [`LICENSE`](LICENSE).

The personal data and the CV's text are not licensed: the name, contact details, profile, experience, certifications and skills, in the page, in its `i18n` dictionaries, in the structured data and in `og-image-2.jpg`, remain the author's, all rights reserved. The Coabana name and brand, which the page cites, are not licensed either. A fork that reuses the code replaces all of them with its own.
The personal data and the CV's text are not licensed: the name, contact details, profile, experience, certifications and skills, in the page, in its `i18n` dictionaries, in the structured data, in `og-image-2.jpg` and in its source `.github/og-card/card.html`, remain the author's, all rights reserved. The Coabana name and brand, which the page cites, are not licensed either. A fork that reuses the code replaces all of them with its own.
Loading