Mettre à jour le site personnel

Modifier directement le HTML pour ajouter des articles, présentations ou services; gérer les grilles hero/actualités/recherche et les images sur un site de recherche statique.

Spar Skills Guide Bot
DeveloppementIntermédiaire
3029/08/2026
Claude CodeCursorWindsurfCopilotCodex
#website-update#html#static-site#content-management#academic-site

Recommandé pour


name: update-website description: Make changes to kfzyqin.github.io — add an accepted paper, talk, or service item; add or re-crop card art; reorder the hero or the news/research grids; or adjust the about-page visitor widgets. Use for any edit to index.html, research.html, their page-2 files, or about.html / about-cn.html.

Updating kfzyqin.github.io

A static personal research site — no build step, no framework. Edit the HTML directly and open it in a browser.

Site map

| File | Holds | | --- | --- | | index.html | Hero slider (5 slides) + news grid (12 bricks) | | index-page-2.html | News overflow | | research.html | Publication catalogue (12 bricks), newest first | | research-page-2.html | Research overflow | | about.html / about-cn.html | Bio, Experience, Services, Visitor Analytics. The -cn file mirrors it with translated strings |

Shared assets: css/styles.css (one large file, sections marked with # Name banner comments), js/main.js (theme JS, incl. the Swiper hero), js/visitor-map.js, resources/<slug>/ for images.

Invariants

Every addition prepends at the top and pushes the tail off the bottom:

| Surface | Capacity | Overflow goes to | | --- | --- | --- | | Hero slider | 5 slides | dropped from the hero | | News grid | 12 bricks | index-page-2.html (top) | | Research grid | 12 bricks | research-page-2.html (top) |

An item in the hero must NOT also have a news brick. The hero and the masonry below it are one continuous feed, so a paper in the hero is deleted from the news grid, and a paper demoted out of the hero gets a news brick back. research.html is exempt — it is a catalogue, not the feed, so hero papers keep their research card there.

Adding a hero slide therefore usually means: prepend the slide, drop the oldest slide, delete that paper's news brick, and pull a brick back from page 2 to restore the 12.

Not every item hits every surface. A paper acceptance → hero (or news brick) + research card. A talk, an area-chair role, or a conference photo → news grid only.

Ordering

  • News grid runs strictly newest-first by the YYYY-MM in each byline. A new entry's date must be >= the current top entry's.
  • Hero order is an editorial choice, not chronological — ask rather than assume.
  • Research leads with the same papers the hero leads with, then follows the news chronology. Research-only papers (no news entry) keep their relative position.

1. Card art

Images live at resources/<slug>/coverimage.jpg, where <slug> is a short topic name (fedllm, sovtr, fakenews, geodano, handcraft). One image serves the hero slide, the news brick, and the research brick.

Target is an exact 4:5 ratio, matching aspect-ratio: 4 / 5 in css/styles.css (search for "Masonry Thumbnail Uniformity"). Anything else still renders, but object-fit: cover center-crops it in the browser.

Crop, never squash, and never upscale. Run the helper next to this file, which collects every image the grids actually reference and normalizes each one:

python3 .claude/skills/update-website/normalize-card-art.py [--dry-run]

Its rule, if you need to apply it by hand:

cw = int(min(W, H * 0.8)) // 4 * 4     # snap to a multiple of 4 so H is a whole number
ch = cw * 5 // 4
sips -s format jpeg -s formatOptions 50 --cropToHeightWidth <ch> <cw> file.jpg
sips -s format jpeg -s formatOptions 50 -z 1500 1200 file.jpg   # ONLY if cw > 1200

For a brand-new poster, crop straight out of the source into place:

mkdir -p resources/<slug>
# NOTE: sips takes HEIGHT before WIDTH, and crops from the center.
sips -s format jpeg -s formatOptions 50 --cropToHeightWidth 1400 1120 <source>.png \
  --out resources/<slug>/coverimage.jpg

Why those numbers:

  • Never upscale. Cards render a few hundred CSS pixels wide, so an 800x1066 source becomes 800x1000 and stops there. Forcing everything up to 1200x1500 was tried once: it softened every image and tripled index.html's art to 5 MB for no visible gain.
  • formatOptions 50 is what sips needs to reproduce the bytes-per-pixel the existing art was already encoded at (~0.07-0.24 B/px measured across a sample). Anything higher re-encodes files larger than their source while looking identical.
  • The script is idempotent — a file already at an exact 4:5 within the cap is left untouched, so re-running never stacks generation loss.

Tooling constraints on this machine:

  • sips is the only image tool — no ImageMagick, no Pillow.
  • sips can read webp but cannot write it. Five research cards have a .webp sibling served via <picture><source>. Leave them alone: a 3:4 webp in a 4:5 box is center-cropped by object-fit: cover to pixel-for-pixel what our sips crop produces, so there is nothing to fix and the bandwidth saving is free.
  • Never ship a multi-megabyte PNG. Convert to JPEG and update the src — two 1024x1536 PNGs were 2.2 and 2.8 MB and became 87 KB and 140 KB.
  • pdftotext (for reading a paper's title and abstract) is at /opt/homebrew/bin/pdftotext; Homebrew itself is at /opt/homebrew/bin/brew, not on PATH. Do not guess a title from a filename, and do not guess which poster belongs to which paper from the artwork — read the abstracts and match. That guess has been wrong before.

2. Hero slide (index.html)

Insert before the current first slide (the comment right after <div class="swiper-wrapper">), then delete slides from the end until 5 remain.

Keep the title short — a noun phrase, matching neighbours like "Set-of-Vision-Text Rewards" or "LMOD+ Ophthalmology Benchmark" — and the description to two lines. Name the contribution, not just the topic.

                        <!-- Short Name -->
                        <article class="hero__slide swiper-slide">
                            <div class="hero__entry-image"
                                style="background-image: url('resources/<slug>/coverimage.jpg');"></div>
                            <div class="hero__entry-text">
                                <div class="hero__entry-text-inner">
                                    <div class="hero__entry-meta">
                                        <span class="cat-links">
                                            <a href="URL">VENUE YEAR</a>
                                        </span>
                                    </div>
                                    <h2 class="hero__entry-title">
                                        <a href="URL">
                                            Short Title
                                        </a>
                                    </h2>
                                    <p class="hero__entry-desc">
                                        Two lines on what the work does.
                                    </p>
                                    <a class="hero__more-link" href="URL">Read More</a>
                                </div>
                            </div>
                        </article>

Journal names go in full in cat-links ("ACM Transactions on Computing for Healthcare", "npj Digital Medicine 2026"), not abbreviated. The hero image renders as a background-size: cover panel filling the left 50% of the slide, so portrait posters work.

3. News brick (index.htmlindex-page-2.html)

Prepend after <div class="grid-sizer"></div>, then move the trailing bricks (everything from the 13th onward, up to </div> <!-- end bricks-wrapper -->) to the top of index-page-2.html, right after its grid-sizer, preserving order.

News bricks use Time: plus a YYYY-MM date. The title is a headline ("X Accepted by Y"), not the paper title. The excerpt is 2-4 sentences, first person, usually thanking collaborators by first name.

                        <!-- Short Name VENUE YEAR -->
                        <article class="brick entry" data-animate-el>

                            <div class="entry__thumb">
                                <a href="URL" class="thumb-link">
                                    <img src="resources/<slug>/coverimage.jpg" loading="lazy" alt="">
                                </a>
                            </div> <!-- end entry__thumb -->

                            <div class="entry__text">
                                <div class="entry__header">
                                    <div class="entry__meta">
                                        <span class="cat-links">
                                            <a href="URL">Paper</a>
                                        </span>
                                        <span class="byline">
                                            Time:
                                            <a href="VENUE_URL">2026-08</a>
                                        </span>
                                    </div>
                                    <h1 class="entry__title"><a href="URL">X Accepted by Y</a></h1>
                                </div>
                                <div class="entry__excerpt">
                                    <p>
                                        Our paper on ... has been accepted by ... I'm grateful to have collaborated
                                        with an amazing team: ...
                                    </p>
                                </div>
                                <a class="entry__more-link" href="URL">Read More</a>
                            </div> <!-- end entry__text -->

                        </article> <!-- end article -->

cat-links is the item type: Paper, Presentation, Talk, Service, or Career.

4. Research brick (research.htmlresearch-page-2.html)

Same prepend-and-cascade, with three differences from a news brick:

  • byline label is On: and holds a venue name (EMNLP Findings 2026), not a date
  • entry__title is the full paper title
  • the closing comment is named: </article> <!-- end SoVTR -->, not <!-- end article -->

The excerpt is a 3-5 sentence technical summary drawn from the abstract — what problem, what method, what result — not a first-person announcement.

Note research.html has no grid-sizer div (the other three pages do); prepend before the first entry comment instead. A card with a webp sibling wraps the img:

                                    <picture>
                                        <source srcset="resources/<slug>/coverimage.webp" type="image/webp">
                                        <img src="resources/<slug>/coverimage.jpg" loading="lazy" alt="">
                                    </picture>

5. Visitor Analytics (about pages)

about.html and about-cn.html end with a .analytics-section: one regular column (.analytics-stack) of two full-width .analytics-cards — the Flag Counter map, then the country flag grid. Styling lives in css/styles.css under # Visitor Analytics; there is no inline <style> and there must not be one again.

Both widgets are remote images on counter a24d, path-segment parameters:

map:   /map/a24d/size_l/txt_000000/border_CCCCCC/pageviews_1/viewers_0/flags_0/
flags: /count2/a24d/bg_FFFFFF/txt_000000/border_CCCCCC/columns_8/maxflags_250/
       viewers_Visitors/labels_0/pageviews_1/flags_1/percent_0/

size_s|m|l map size; columns_N grid width; maxflags_N cap; labels_0 flag+count only; percent_0 no percentages; flags_1 render the grid; pageviews_1 show the total.

Sizing, measured from live fetches:

width  = 8 + 77 × columns        (624px at columns_8 — independent of flag count)
height = 80 + 18 × rows          (rows = ceil(flags / columns))

Because width is fixed and the card is 760px wide, the grid never scales down; it only grows taller as countries accumulate (16 flags = 116px, 100 = 314px, the 250 cap = 656px). That is intended — do not reintroduce a fixed-size box around it. A square 250x250 wrapper is exactly what once shrank the whole image until the flags were unreadable. columns_8 (624px) is near the widest that fits; columns_10 (778px) would start scaling down.

.visitor-map-frame carries no styling — it is purely the hook js/visitor-map.js walks up to with closest(). That script watches every img[data-visitor-map], swaps in a .visitor-map-fallback placeholder on error, and retries at 5s/20s/60s. Both widgets carry the attribute; a new one must too, plus data-fallback-text.

The four grid pages carry a hidden Flag Counter pixel so page views outside the about pages register on the same counter.

6. After editing css/styles.css

python3 .claude/skills/update-website/bump-css-version.py

Every page links the stylesheet as css/styles.css?v=<hash of the file>. Browsers cache CSS aggressively -- file:// most of all -- and stale CSS repeatedly made correct edits look like they had not applied, costing several debugging rounds chasing a bug that was not in the code. The hash is content-derived, so it changes exactly when the CSS does. Run this after any stylesheet edit, before asking anyone to look at the result.

7. Conventions and gotchas

  • No link yet? Use href="#0" — the repo's convention for a dead link (see the disabled pagination arrows). Never leave href empty or omitted.
  • The -page-2.html files use an older, shallower-indented style with single-line <h1 class="entry__title">. Paste moved blocks verbatim; do not reformat page 2.
  • <div class="grid-sizer"> is vestigial (display: none) but is still the reliable insertion anchor.
  • sips takes height before width, everywhere.
  • HTML parsers report one unclosed <div id="page"> on every page (two on the about pages). This is pre-existing in the template — verify against git show HEAD:<file> rather than trying to fix it.
  • Prefer a Python script over hand-editing when moving blocks — split on the entry comment, bound the region with </div> <!-- end bricks-wrapper -->, and assert the expected class="brick entry" count before writing.

8. Verify

for f in index.html index-page-2.html research.html research-page-2.html; do
  echo "$f bricks=$(grep -c 'class=\"brick entry\"' $f)"
done                                                    # index & research must be 12
grep -c 'class="hero__slide swiper-slide"' index.html   # must be 5

# hero images and news-grid images must be DISJOINT sets
# every card image an exact 4:5; every resources/ path resolves; tag balance == HEAD
python3 .claude/skills/update-website/normalize-card-art.py --dry-run   # all "ok"

python3 -m http.server 8000   # then eyeball every page and every breakpoint

Check the responsive column counts too: 4 cols default, 3 at <=1500px, 2 at <=1080px, 1 at <=700px.

Skills similaires