Maintaining skhgroup.net

index.html   the one-pager (~52 KB), plain static HTML
assets/style.css           all styling, shared by the one-pager and the blog
covers/                    journal cover art, 760 px wide
_data/covers.yml           the cover list — the page is generated from this
_data/people.yml           current lab members — the People page is generated from this
member_form.docx           the form you send to a new member
img/                       PI photo
news.html                  the blog index (served at /news/ , not /news.html)
404.html                   custom not-found page (permalink pinned — see the trap below)
og.png                     1200x630 link-preview card. Referenced by index.html's
                           og:image/twitter:image. If you rename or delete it, every
                           share on LINE, Slack, X, or Facebook shows a broken image.
robots.txt                 points crawlers at the sitemap
img/og/                    1200x630 landscape share cards, one per cover
tools/make_og_cards.py     regenerates them from _data/covers.yml
_posts/                    one markdown file per post   <- the part you edit often
_layouts/                  page shell for blog pages
_config.yml                Jekyll settings

Two halves, deliberately:

You can do all of this from a phone. There is no local install and no build command.

Suggested tags: Cover, Paper, Talk, People, Lab.

Share a post to Facebook or LinkedIn

Every post page has LinkedIn / Facebook / X / Copy link buttons under the article. Click one, or paste the post URL into a post on either platform. The preview card is generated from the page’s Open Graph tags.

Never point image: at a file in covers/. Journal covers are portrait (760x999). Facebook and LinkedIn want 1.91:1 landscape, so a portrait cover gets centre-cropped into a letterbox that cuts off the top and bottom of the artwork. Point at the pre-built landscape card instead:

image: /img/og/Batteries_and_Supercaps_2026.jpg   # 1200x630, cover + caption

Posts with no image: fall back to og.png, which is always a safe default.

Making a card for a new cover. Add the cover to _data/covers.yml first, then either ask Claude for the card, or run it yourself:

pip install pillow pyyaml fonttools
python tools/make_og_cards.py     # reads _data/covers.yml, writes img/og/*.jpg

If the preview looks wrong or stale, the platform has cached the old tags. Force a refresh:

Paste the URL, click Inspect / Scrape Again. LinkedIn caches for about 7 days and there is no way around it other than the inspector, so check the preview before you post, not after.

A post does not have to be an essay. Three sentences announcing a cover is a post. The failure mode for an academic blog is not posts that are too short, it is a “Latest news, March 2024” banner sitting on a 2026 site. Short and alive beats long and abandoned.

Put an image in a post

  1. Resize to ~760 px wide first. A phone photo is 4000 px and 5 MB; the page shows it at 760 px either way, so the other 4.3 MB is pure load time.
  2. Upload it to img/news/ (Add file → Upload files → drag). Lowercase, no spaces: 2026-08-glovebox.jpg.
  3. In the post:
    ![Short description of the photo](/img/news/2026-08-glovebox.jpg)
    

    With a caption:

    <figure>
      <img src="/img/news/2026-08-glovebox.jpg" alt="Short description">
      <figcaption>The caption readers actually see.</figcaption>
    </figure>
    

Paths start with / because baseurl is "" and the site sits at the root of www.skhgroup.net. covers/ is for journal cover art only; put post photos in img/news/.

Update the publication counts

One <h2> and one <div class="note"> in id="publications". Change both together or they will disagree. Refresh the “July 2026” date at the same time.


Two traps that already bit (do not re-introduce)

1. .page is hidden by default. The one-pager is a JS tab switcher: .page{display:none} and JS adds .on to whichever section the hash selects. Blog pages have no such JS, so _layouts/default.html wraps content in <div class="page on">. Drop the on and the blog renders as a nav and a footer with nothing between them.

permalink: in _config.yml ends with a /. In Jekyll that turns on pretty URLs for every HTML page, not just posts: news.html is built to /news/index.html and /news.html returns 404. index.html and feed.xml are exempt, which is why the site root kept working and only the News link broke.

news.html now pins its own permalink: /news/ in the front matter, so it no longer depends on that setting. Link to /news/, never /news.html.

404.html pins permalink: /404.html for the same reason. GitHub Pages only serves a custom 404 from that exact path; without the pin Jekyll would build it to /404/ and you would silently get GitHub’s generic error page back.

Deploying

Commit to github.com/soorathep/Test. GitHub Pages rebuilds on every push. If images do not appear, the folder is missing or misnamed: paths are case-sensitive.

Two settings live in _config.yml and nowhere else:

If the blog breaks after a rename, it is one of those two lines.


Design decisions worth not undoing


Working with Claude on this

Claude cannot reach the repo. Two ways to work:

  1. Claude Code, repo cloned locally. Best option. Say “add these three talks” and it edits the file and commits. No uploads, no round-trips.
  2. Chat. At 61 KB the whole file now fits in a message. Better still, paste only the section that changes and ask for the replacement block.

Give Claude the source, not a summary: the CV, the publisher’s cover file, the Scopus page. Every error worth fixing in this file so far came from someone paraphrasing a source instead of reading it.