Colophon
How this site works
This site uses WordPress for writing articles and serves its pages as static files built in advance. For readers interested in the technology, here is how it is made and what it protects.
Live
Measure this page on your device
The figures below were recorded by your own browser when you opened this page. They are not sent anywhere; they are only shown on this screen. The pass marks follow Google’s thresholds for “good” (LCP 2.5 s, CLS 0.1, INP 200 ms).
- Time until the largest element appears (LCP)
- Layout shift (CLS)
- Time from a tap or click to the response (INP)
- Time until something first appears (FCP)
- Time until the server starts responding (TTFB)
- HTML transferred for this page
- How this page was delivered
Order in which the files arrived
Once the page has finished loading, the files are listed in the order they arrived.
Checking the files delivered
Comparing them with the build record.
Technologies this browser supports
Asking your browser about each one in turn.
- CheckingSmooth transitions between pages
Cross-document View Transitions - CheckingDirection of transitions (forward, back, rising from below)
View Transition Types - CheckingPreloading the next page
Speculation Rules - CheckingDelta delivery from page 2 onward
Compression Dictionary Transport - CheckingScroll-linked motion
Scroll-driven Animations - CheckingStyling based on scroll state
Scroll-state Container Queries - CheckingDelays based on position in the order
sibling-index() - CheckingSpring motion
CSS linear() easing - CheckingSmooth corners
corner-shape: squircle - CheckingLine breaks at phrase boundaries in headings
word-break: auto-phrase - CheckingSpacing between Japanese and Latin text
text-autospace - CheckingAutomatic light and dark themes
light-dark() - CheckingAnimated height when opening and closing
interpolate-size - CheckingMenus without scripts
Popover API - CheckingDetecting the direction of navigation
Navigation API - CheckingSafe writing to the page from scripts
Trusted Types
- Delivery
- Static files (HTML, CSS, images)
- Requests to WordPress
- GET only
- Web fonts loaded
- 0 bytes
- Inline scripts and style attributes
- 0
- Activity reports at build time
- 17 reports
- Last built
01Keeping content and presentation apart
Activity reports are written in the WordPress dashboard, as before. This site reads those posts through the public WordPress REST API, assembles them into pages, and serves them.
The component that reads them is built so that it can only send GET requests, and it has no way to change WordPress data. The previous results are kept locally and requests carry If-None-Match and If-Modified-Since, so posts that have not changed are not fetched again.
For texts other than articles, such as the home and donation pages, the only master copy is a file of fact-checked texts (the Design Layer). Pages load their wording from it without changing a single character. We have a rule not to publish our phone number: at the end of the build, every output document is searched, and if a number is found, the build itself fails.
02How the build works
- Load the Design Layer texts and photos, and the WordPress posts, categories and donation totals
- Sanitize article HTML by keeping only allowed elements and attributes (scripts, embeds and style attributes are removed)
- Convert photos to AVIF and WebP at several widths, adding a hash of the content to each file name
- Write out the HTML of each page, plus Markdown with the same content
- Write out the feeds, sitemap, search index, llms.txt and response header settings
- Check for phone numbers, pre-compress with Brotli, create deltas against the compression dictionary, and record the SHA-256 of every file
The build is TypeScript run directly by Node.js. The only external package is sharp, used to convert images.
03Speed
- Images: AVIF and WebP are provided at several widths with
srcset, and the browser picks the one that fits the screen. Width and height are declared in advance, so the layout does not shift while images load. Until a photo loads, a gradient made from its colors shrunk to 2×2 is laid underneath as a CSS class (no data: URIs are used). - Text: no web fonts are loaded; text is shown in the fonts already on your device. Text takes 0 bytes of transfer.
- Lighter from page 2 onward: the parts that every page shares, such as the header, menu and footer, are combined into a single “compression dictionary,” which the browser fetches just once, when it is idle (Compression Dictionary Transport, RFC 9842). From page 2 onward, only the differences from the dictionary are sent, compressed with Zstandard, so the HTML transferred is about 40% smaller than with Brotli alone, and the donation page shrinks to about 1/10. Browsers that do not support dictionaries still get Brotli, as before.
- Preloading the next page: Speculation Rules are delivered in an HTTP response header, and the browser starts preparing the next page when, for example, the pointer rests on a link. This approach needs no inline script.
- Page transitions: page-to-page navigation within the site uses the browser’s cross-document View Transitions. When you tap a post’s photo in the list of activity reports, that photo changes shape as it moves into the photo at the top of the post. The direction of the transition also depends on where you are going (View Transition Types). Deeper pages and the next post slide in from the right; when you go back with the browser’s Back button or up a level with the breadcrumbs, the page slides in from the left; and the donation page rises from the bottom of the screen. To decide the direction, the page being left records its own level, and the page being opened looks at its own level and its position in the Navigation API history. The header and the donation button at the bottom of the screen stay still, and only the page beneath them changes.
- Measuring real visits: on the live site, the time until the page appears (LCP), layout shift (CLS) and the time from a tap or click to the response (INP) are counted from the values recorded by visitors’ browsers. All that is sent to Google Analytics is each metric’s rating (“good,” “needs improvement” or “poor”) and its value. The aim is to check the speed on real devices and connections, not in a test environment.
- CSS and JavaScript: the styles come in 1 stylesheet, with the order fixed by cascade layers. Scripts are loaded as modules, and only the ones a page needs.
Speculation-Rules: "/speculation.json"
04The road to Kyiv
On the map on the home page, the SVG of the route from Mizushima Port to Kyiv is given pathLength="10000". Counting the length of the route as 10,000 makes 1 unit along the route equal exactly 1 km.
The ambulance’s position, the yellow line showing how far it has come, and the blue line that appears when you change the donation amount are all calculated by CSS from typed custom properties (--km-now and --you). JavaScript only rewrites --you as the amount is entered; it does not calculate the ambulance’s coordinates.
.map .vehicle {
offset-path: var(--route);
offset-distance: calc(
min(10000, (var(--km-now) + var(--you)) * var(--drive)) / 100 * 1%
);
}
As the map scrolls into view, the ambulance drives from the port to today’s position. This is a scroll-driven animation (animation-timeline: view()), also drawn with CSS alone.
05Motion, and settings that reduce it
Sections appearing and photos slowly zooming are CSS animations linked to the scroll position. If you have turned on “Reduce motion” or a similar setting on your device, they are shown without motion. The same setting also stops the transitions between pages.
Buttons and cards move with a spring effect. About 30 points were taken from the equation of a damped oscillation and listed in CSS linear(): press, and it sinks at once; release, and it overshoots a little before it settles. No script is used.
--spring: linear(0, 0.034, 0.118, 0.231, … 1.046, … 1);
.btn { transition: transform .45s var(--spring); }
06Security rules
- Content Security Policy: scripts and styles are loaded only from this site’s own files. There is not a single inline script,
styleattribute or<style>element. Even values that change are passed through CSS classes and custom properties. - Trusted Types:
require-trusted-types-for 'script'is enabled, and not a single policy is allowed. Scripts cannot write HTML withinnerHTML, and even site search results are shown by building DOM elements one at a time. - Subresource Integrity: CSS and JavaScript files carry the SHA-384 of their content in an
integrityattribute. If a file is altered on its way to you, the browser will not run it. - Other response headers: Cross-Origin-Opener-Policy and Cross-Origin-Resource-Policy limit interaction with other sites, and Permissions-Policy denies the use of the camera, microphone, location and similar features.
- Article content: HTML received from WordPress is published only after everything but the allowed elements, attributes and URL forms (http, https, mailto and paths within the site) has been removed.
07Typesetting
Spacing between Japanese and Latin text (text-autospace), trimming of space around punctuation (text-spacing-trim) and phrase-based line breaks in headings (word-break: auto-phrase) are left to the browser. Browsers that do not support them show the text with ordinary typesetting.
08Machine-readable formats
Add index.md to the end of any page’s address to read the same content in Markdown (for example, this page in Markdown). The pages also include structured data (JSON-LD).
/feed.xmlActivity reports as RSS 2.0 (in Japanese)/feed.jsonActivity reports as JSON Feed 1.1 (in Japanese)/llms.txtGuide to the Markdown version of each page (in Japanese)/sitemap.xmlSitemap/search-index.jsonIndex for site search. The search runs in your browser/.well-known/build.jsonBuild record: when the site was built, and the size and SHA-256 of every published file
09Even without JavaScript
The text, the donation links, the menu and the FAQ answers that open and close all work with JavaScript turned off. The menu is made with the HTML popover attribute, and the FAQ answers with the <details> element. JavaScript is used only where it adds convenience, such as the amount control on the map and site search.
