Skip to content

Development notes

Keeping work-category state in the URL fragment

How native work-category links, CSS fallbacks, and scripts handle sharing, reloads, Back, and language switching, including an observed stale-fragment limitation.

林予晴
Published
  • URL
  • HTML
  • State design
  • Progressive enhancement

If a reader switches the work page to design and shares the URL, but the recipient lands on the default category, the choice exists only in the original view.

The site put work categories in the URL fragment, or hash. That gave the reader’s current choice a representation that could be shared, reloaded, and revisited, but it did not make every surrounding link stay in sync automatically. On October 26, 2026, I checked the category controls, no-script fallback, and language links together to see where that state was preserved.

Choose which state belongs in the URL

The work page’s category choice determines which group of content a reader sees, so it makes sense to keep it in the URL. A card’s momentary offset, the image wall’s height, and the progress of an entrance animation are presentation details; they do not need to become part of the public URL.

All three categories used the same work-page document. These are their Traditional Chinese URL examples:

/about/work#development
/about/work#design
/about/work#aigc

A fragment is the part of a URI that starts with #. It is not sent to the server when the resource is requested; the client handles it after obtaining the resource. [3] The site therefore did not build a separate static page for each of these three categories or fetch different data from the server based on the hash.

That choice suited the content’s scale at the time: each category’s body was already in the HTML, and the URL only needed to express which section the reader wanted to see. A move to large data queries, search, or combinations of filters would require revisiting the roles of paths, query parameters, and data fetching. This small example is not an answer for every filter interface.

The development, design, and AIGC controls were anchors with fragments, not imitation links backed only by click handlers. Each destination also had a matching section ID.

With scripts enabled, an ordinary click updated location.hash, then synchronized the visible section, category styling, and aria-current. Clicks with modifier keys bypassed that interception and kept the browser’s native behavior.

These controls were not declared as ARIA tabs and did not implement the arrow-key rules for switching tabs. They were native links controlling the current reading section. Arranging them in a row does not justify assigning them a keyboard contract the implementation does not provide.

Category switching and image-wall layout were also separate tasks. The hash determined which section appeared; the work page’s own scheduling and observers still handled card columns and relayout after images loaded. The URL did not need the coordinates of every card.

The CSS fallback need not match scripted mode

With page scripts disabled, CSS could still use :target and :has() to identify the target section and hide the other categories in browsers that supported those selectors.

I disabled page scripts in local Edge, opened the design category directly, and then followed the native link to AIGC. The corresponding content appeared in both cases. This mode did not run the scripted image-wall layout or add the scripted accessibility state for the current category.

One difference needs to remain explicit: without a hash, the no-script version left all category content visible, while the scripted version defaulted to development. An unrecognized hash also made the script fall back to development.

Scenario Display strategy
Scripts enabled; hash selects design Show the design section
Scripts enabled; category hash absent or unrecognized Default to the development section
No scripts; selectors supported and a matching target exists CSS shows the target category
No scripts; no target Keep every category readable

This fallback prioritized access to content rather than pretending that every environment received exactly the same enhancements. The Edge observation also cannot establish the behavior of environments that do not support the relevant CSS.

Back and reload must recover the same choice

Changing the view only inside a click handler is not enough. A reader may open a URL with a hash directly or use Back instead of clicking the category control again.

The script read the hash during initialization and listened for hashchange and popstate to bring the visible state back into line with the URL. In the local observation, I switched from design to AIGC, then used browser history to go back. The view returned to design, and the design category was still displayed after a reload.

Those observations established synchronization between category and URL, not that every back-forward cache scenario had been checked. Keeping state in the URL still requires correct initialization and event handling to restore the corresponding view.

Another entry point may hold an old state snapshot

Checking language switching exposed a more specific limitation than whether the hash had been copied at all. The shared script updated language links during initialization and when the desktop language menu opened or closed. It did not synchronize every entry point after each work-category change.

After opening design and switching to AIGC, I found that the English link inside the mobile navigation still held the initial design hash. When I triggered an update through the desktop language menu instead, the link carried the current AIGC hash.

Copying the URL during initialization cannot therefore be described as “language switching always preserves the current category.” This was a limitation of the implementation, not advice to open the desktop menu every time, and this article did not fix it.

To complete this kind of state contract, I would first list the entry points that depend on the state, then decide whether to update them when the state changes or just before an entry point is used. That makes stale values easier to track than storing a separate category beside each link.

How static Astro pages keep Canvas, GSAP, and local interactions describes the boundaries of interaction responsibilities. This note follows where the state goes. A hash makes the category part of the URL; sharing it, returning to it, and keeping other entry points in sync still need to be checked separately.

Sources

[3] https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Fragment — MDN: URI fragment