v4: the Times style gets a masthead and a face for each job — one condensed serif did everything, buttons and tabs included, under a plain header bar. The site's name is now a blackletter nameplate (Manufacturing Consent), its emoji beside it on the same baseline at the size of a printer's mark. From 860 pixels up it takes the middle of a row of its own, the buttons to its right, and what a page puts in the header, a feed's tabs or the open search field, runs underneath between a hairline and a double rule; a page that puts nothing there keeps one row and a single ink rule. Instrument Serif stays for headlines, at its one weight and larger sizes, Newsreader sets what is read at length (summaries, notes, comments), and Libre Franklin everything the interface says: controls, tabs, datelines, bylines, labels (--font-ui, listed in themes/nyt.css). Tabs are sentence case with an ink underline on the open one. The comments, the related dumps, a profile's sections and a page's groups open on an ink rule with their label in bold capitals under it. A dump's categories are small capital kickers, where they were grey chips. Buttons and fields take a 3px radius, pictures, cards and sheets staying square. The header sits on the page's color in the dark scheme too. In the journal each cell draws its four rules as a 1px shadow in a 1px gap, where it drew its top and left edges: a last row the dumps don't fill was left open on its right, and the row above it unruled over the empty tracks. A page's title keeps a word whole unless it is wider than the line. deno.lock also takes the key format of the Deno that wrote it, with no version changed beyond the three font packages.
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 53s
Build and Deploy Static Documentation Site / build-and-push (push) Successful in 26s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
khannurien
2026-10-12 09:26:09 +00:00
parent 3e15e9a4f2
commit d54ae554a4
4 changed files with 570 additions and 148 deletions

81
deno.lock generated
View File

@@ -29,34 +29,37 @@
"jsr:@std/path@^1.1.6": "1.1.6", "jsr:@std/path@^1.1.6": "1.1.6",
"jsr:@std/testing@^1.0.21": "1.0.21", "jsr:@std/testing@^1.0.21": "1.0.21",
"jsr:@tajpouria/cors@^1.2.1": "1.2.1", "jsr:@tajpouria/cors@^1.2.1": "1.2.1",
"npm:@eslint/js@^10.0.1": "10.0.1_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0", "npm:@eslint/js@^10.0.1": "10.0.1_eslint@10.11.0_jiti@2.7.0",
"npm:@fontsource-variable/fraunces@5.3.0": "5.3.0", "npm:@fontsource-variable/fraunces@5.3.0": "5.3.0",
"npm:@fontsource-variable/jetbrains-mono@5.3.0": "5.3.0", "npm:@fontsource-variable/jetbrains-mono@5.3.0": "5.3.0",
"npm:@fontsource-variable/libre-franklin@5.3.0": "5.3.0",
"npm:@fontsource-variable/newsreader@5.3.0": "5.3.0",
"npm:@fontsource-variable/saira@5.3.0": "5.3.0", "npm:@fontsource-variable/saira@5.3.0": "5.3.0",
"npm:@fontsource-variable/space-grotesk@5.3.0": "5.3.0", "npm:@fontsource-variable/space-grotesk@5.3.0": "5.3.0",
"npm:@fontsource/instrument-serif@5.3.0": "5.3.0", "npm:@fontsource/instrument-serif@5.3.0": "5.3.0",
"npm:@fontsource/manufacturing-consent@5.3.0": "5.3.0",
"npm:@lingui/cli@*": "6.8.0", "npm:@lingui/cli@*": "6.8.0",
"npm:@lingui/cli@6.8.0": "6.8.0", "npm:@lingui/cli@6.8.0": "6.8.0",
"npm:@lingui/conf@6.8.0": "6.8.0", "npm:@lingui/conf@6.8.0": "6.8.0",
"npm:@lingui/core@6.8.0": "6.8.0_@babel+core@7.29.7", "npm:@lingui/core@6.8.0": "6.8.0_@babel+core@7.29.7",
"npm:@lingui/format-po@6.8.0": "6.8.0", "npm:@lingui/format-po@6.8.0": "6.8.0",
"npm:@lingui/react@6.8.0": "6.8.0_react@19.3.0_@babel+core@7.29.7", "npm:@lingui/react@6.8.0": "6.8.0_react@19.3.0_@babel+core@7.29.7",
"npm:@lingui/swc-plugin@6.7.0": "6.7.0_@lingui+core@6.8.0__@babel+core@7.29.7_@babel+core@7.29.7", "npm:@lingui/swc-plugin@6.7.0": "6.7.0_@lingui+core@6.8.0_@babel+core@7.29.7",
"npm:@lingui/vite-plugin@6.8.0": "6.8.0_@babel+core@7.29.7_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0_@types+node@26.6.3_jiti@2.7.0", "npm:@lingui/vite-plugin@6.8.0": "6.8.0_@babel+core@7.29.7_vite@8.3.1_@types+node@26.6.3_jiti@2.7.0",
"npm:@resvg/resvg-wasm@2.6.2": "2.6.2", "npm:@resvg/resvg-wasm@2.6.2": "2.6.2",
"npm:@resvg/resvg-wasm@^2.6.2": "2.6.2", "npm:@resvg/resvg-wasm@^2.6.2": "2.6.2",
"npm:@testing-library/dom@^10.4.2": "10.4.2", "npm:@testing-library/dom@^10.4.2": "10.4.2",
"npm:@testing-library/jest-dom@^7.0.1": "7.0.1_@testing-library+dom@10.4.2_vitest@5.0.2__@types+node@26.6.3__@vitest+coverage-v8@5.0.2__jsdom@30.1.1__vite@8.3.1___@types+node@26.6.3___jiti@2.7.0_@vitest+coverage-v8@5.0.2__vitest@5.0.2__@types+node@26.6.3__jsdom@30.1.1__vite@8.3.1___@types+node@26.6.3___jiti@2.7.0", "npm:@testing-library/jest-dom@^7.0.1": "7.0.1_@testing-library+dom@10.4.2_vitest@5.0.2_@vitest+coverage-v8@5.0.2",
"npm:@testing-library/react@^16.3.3": "16.3.3_@testing-library+dom@10.4.2_@types+react@19.3.0_@types+react-dom@19.3.0__@types+react@19.3.0_react@19.3.0_react-dom@19.3.0__react@19.3.0", "npm:@testing-library/react@^16.3.3": "16.3.3_@testing-library+dom@10.4.2_@types+react@19.3.0_@types+react-dom@19.3.0_react@19.3.0_react-dom@19.3.0",
"npm:@testing-library/user-event@^14.6.7": "14.6.7_@testing-library+dom@10.4.2", "npm:@testing-library/user-event@^14.6.7": "14.6.7_@testing-library+dom@10.4.2",
"npm:@types/node@^26.6.3": "26.6.3", "npm:@types/node@^26.6.3": "26.6.3",
"npm:@types/react-dom@^19.3.0": "19.3.0_@types+react@19.3.0", "npm:@types/react-dom@^19.3.0": "19.3.0_@types+react@19.3.0",
"npm:@types/react@^19.3.0": "19.3.0", "npm:@types/react@^19.3.0": "19.3.0",
"npm:@vitejs/plugin-react-swc@^4.3.3": "4.3.3_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0_@types+node@26.6.3_jiti@2.7.0", "npm:@vitejs/plugin-react-swc@^4.3.3": "4.3.3_vite@8.3.1_@types+node@26.6.3_jiti@2.7.0",
"npm:@vitest/coverage-v8@^5.0.2": "5.0.2_vitest@5.0.2_@types+node@26.6.3_jsdom@30.1.1_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0", "npm:@vitest/coverage-v8@^5.0.2": "5.0.2_vitest@5.0.2_@types+node@26.6.3_jsdom@30.1.1_vite@8.3.1",
"npm:emojibase-data@17.0.0": "17.0.0_emojibase@17.0.0", "npm:emojibase-data@17.0.0": "17.0.0_emojibase@17.0.0",
"npm:eslint-plugin-react-hooks@^7.1.1": "7.1.1_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0", "npm:eslint-plugin-react-hooks@^7.1.1": "7.1.1_eslint@10.11.0_jiti@2.7.0",
"npm:eslint-plugin-react-refresh@~0.5.7": "0.5.7_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0", "npm:eslint-plugin-react-refresh@~0.5.7": "0.5.7_eslint@10.11.0_jiti@2.7.0",
"npm:eslint@^10.11.0": "10.11.0_jiti@2.7.0", "npm:eslint@^10.11.0": "10.11.0_jiti@2.7.0",
"npm:frimousse@0.4": "0.4.0_react@19.3.0_typescript@6.0.3", "npm:frimousse@0.4": "0.4.0_react@19.3.0_typescript@6.0.3",
"npm:globals@^17.12.0": "17.12.0", "npm:globals@^17.12.0": "17.12.0",
@@ -68,15 +71,15 @@
"npm:react-dom@^19.3.0": "19.3.0_react@19.3.0", "npm:react-dom@^19.3.0": "19.3.0_react@19.3.0",
"npm:react-hook-form@^7.89.0": "7.89.0_@types+react@19.3.0_react@19.3.0", "npm:react-hook-form@^7.89.0": "7.89.0_@types+react@19.3.0_react@19.3.0",
"npm:react-markdown@^10.1.0": "10.1.0_@types+react@19.3.0_react@19.3.0", "npm:react-markdown@^10.1.0": "10.1.0_@types+react@19.3.0_react@19.3.0",
"npm:react-router@^8.4.0": "8.4.0_react@19.3.0_react-dom@19.3.0__react@19.3.0", "npm:react-router@^8.4.0": "8.4.0_react@19.3.0_react-dom@19.3.0",
"npm:react@^19.3.0": "19.3.0", "npm:react@^19.3.0": "19.3.0",
"npm:remark-gfm@^4.0.1": "4.0.1", "npm:remark-gfm@^4.0.1": "4.0.1",
"npm:typescript-eslint@^8.70.1": "8.70.1_eslint@10.11.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0", "npm:typescript-eslint@^8.70.1": "8.70.1_eslint@10.11.0_typescript@6.0.3_jiti@2.7.0",
"npm:typescript@~6.0.3": "6.0.3", "npm:typescript@~6.0.3": "6.0.3",
"npm:vite@*": "8.3.1_@types+node@26.6.3_jiti@2.7.0", "npm:vite@*": "8.3.1_@types+node@26.6.3_jiti@2.7.0",
"npm:vite@^8.3.1": "8.3.1_@types+node@26.6.3_jiti@2.7.0", "npm:vite@^8.3.1": "8.3.1_@types+node@26.6.3_jiti@2.7.0",
"npm:vitest@*": "5.0.2_@types+node@26.6.3_@vitest+coverage-v8@5.0.2_jsdom@30.1.1_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0", "npm:vitest@*": "5.0.2_@types+node@26.6.3_@vitest+coverage-v8@5.0.2_jsdom@30.1.1_vite@8.3.1",
"npm:vitest@^5.0.2": "5.0.2_@types+node@26.6.3_@vitest+coverage-v8@5.0.2_jsdom@30.1.1_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0" "npm:vitest@^5.0.2": "5.0.2_@types+node@26.6.3_@vitest+coverage-v8@5.0.2_jsdom@30.1.1_vite@8.3.1"
}, },
"jsr": { "jsr": {
"@db/sqlite@0.13.0": { "@db/sqlite@0.13.0": {
@@ -382,14 +385,14 @@
"@csstools/color-helpers@6.1.2": { "@csstools/color-helpers@6.1.2": {
"integrity": "sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==" "integrity": "sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow=="
}, },
"@csstools/css-calc@3.4.1_@csstools+css-parser-algorithms@4.0.1__@csstools+css-tokenizer@4.0.2_@csstools+css-tokenizer@4.0.2": { "@csstools/css-calc@3.4.1_@csstools+css-parser-algorithms@4.0.1_@csstools+css-tokenizer@4.0.2": {
"integrity": "sha512-EtC7SoN1j6J4E4DCwg5QgbO5TGxgxIA1RXqe+W+qUM+BUcezx9wT+/tiQ/WO2yCX4i5X+Cuf9ciJ22aP4UEwWw==", "integrity": "sha512-EtC7SoN1j6J4E4DCwg5QgbO5TGxgxIA1RXqe+W+qUM+BUcezx9wT+/tiQ/WO2yCX4i5X+Cuf9ciJ22aP4UEwWw==",
"dependencies": [ "dependencies": [
"@csstools/css-parser-algorithms", "@csstools/css-parser-algorithms",
"@csstools/css-tokenizer" "@csstools/css-tokenizer"
] ]
}, },
"@csstools/css-color-parser@4.2.4_@csstools+css-parser-algorithms@4.0.1__@csstools+css-tokenizer@4.0.2_@csstools+css-tokenizer@4.0.2": { "@csstools/css-color-parser@4.2.4_@csstools+css-parser-algorithms@4.0.1_@csstools+css-tokenizer@4.0.2": {
"integrity": "sha512-DyefytAZ735mX4Dq/WcDAXFtXhaEFvme0ZS9tVEBAc2whxUthXr0R0L2rmEPm59SrMBkGrFzQviBXMs/UtnABQ==", "integrity": "sha512-DyefytAZ735mX4Dq/WcDAXFtXhaEFvme0ZS9tVEBAc2whxUthXr0R0L2rmEPm59SrMBkGrFzQviBXMs/UtnABQ==",
"dependencies": [ "dependencies": [
"@csstools/color-helpers", "@csstools/color-helpers",
@@ -416,7 +419,7 @@
"@csstools/css-tokenizer@4.0.2": { "@csstools/css-tokenizer@4.0.2": {
"integrity": "sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==" "integrity": "sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw=="
}, },
"@eslint-community/eslint-utils@4.9.1_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0": { "@eslint-community/eslint-utils@4.9.1_eslint@10.11.0_jiti@2.7.0": {
"integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
"dependencies": [ "dependencies": [
"eslint", "eslint",
@@ -446,7 +449,7 @@
"@types/json-schema" "@types/json-schema"
] ]
}, },
"@eslint/js@10.0.1_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0": { "@eslint/js@10.0.1_eslint@10.11.0_jiti@2.7.0": {
"integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
"dependencies": [ "dependencies": [
"eslint" "eslint"
@@ -474,6 +477,12 @@
"@fontsource-variable/jetbrains-mono@5.3.0": { "@fontsource-variable/jetbrains-mono@5.3.0": {
"integrity": "sha512-F32xpS2NsGYoQi2ADSkKTgpJj7ozajsGgDJ8woTnqjmIB+dxDIqImjl4pXZVEExu8UFZ2ndhmX18EBS/hdz3Lw==" "integrity": "sha512-F32xpS2NsGYoQi2ADSkKTgpJj7ozajsGgDJ8woTnqjmIB+dxDIqImjl4pXZVEExu8UFZ2ndhmX18EBS/hdz3Lw=="
}, },
"@fontsource-variable/libre-franklin@5.3.0": {
"integrity": "sha512-PEQemCKVRtkLZJNpfvR4wG7x87c8iG05XJuEaX59ogMcAQpdtCZz/NXZUrXLJyvyx3/HD70W4LUppWLBdEdU2g=="
},
"@fontsource-variable/newsreader@5.3.0": {
"integrity": "sha512-rrzYi43qMpbzwuFtf9OkWH8sxAPVPcQQQEwXpPtwaKYeJ8yVg5aLs5kawmo1f2Q1t1M38TLmEKCkGVDsYwgdFw=="
},
"@fontsource-variable/saira@5.3.0": { "@fontsource-variable/saira@5.3.0": {
"integrity": "sha512-C1rm7n1OuEx4Se/SAB/5EeUuCviGFFnYTr5vxuQQm2YIOfgbQWx/P6qqhdXlVCBRm/1UzNWCtDK5jNbVmO6W2w==" "integrity": "sha512-C1rm7n1OuEx4Se/SAB/5EeUuCviGFFnYTr5vxuQQm2YIOfgbQWx/P6qqhdXlVCBRm/1UzNWCtDK5jNbVmO6W2w=="
}, },
@@ -483,6 +492,9 @@
"@fontsource/instrument-serif@5.3.0": { "@fontsource/instrument-serif@5.3.0": {
"integrity": "sha512-mDiaIg0u67sYV59fie92Wz4sM8UiVlbL7fLxnFPCKkX15DMASEnQTREbaP5S5/3DCcsoAOcQ0sV9E4AeY4QqYQ==" "integrity": "sha512-mDiaIg0u67sYV59fie92Wz4sM8UiVlbL7fLxnFPCKkX15DMASEnQTREbaP5S5/3DCcsoAOcQ0sV9E4AeY4QqYQ=="
}, },
"@fontsource/manufacturing-consent@5.3.0": {
"integrity": "sha512-p/omJ13hFgV6+THlT370nyHjlMkrZWkKPiMSZHROcyFoylVvazo19Rp9KJhwudWqcbkClgzjEryvC330y4Pr6Q=="
},
"@humanfs/core@0.19.2": { "@humanfs/core@0.19.2": {
"integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
"dependencies": [ "dependencies": [
@@ -647,14 +659,14 @@
"use-sync-external-store" "use-sync-external-store"
] ]
}, },
"@lingui/swc-plugin@6.7.0_@lingui+core@6.8.0__@babel+core@7.29.7_@babel+core@7.29.7": { "@lingui/swc-plugin@6.7.0_@lingui+core@6.8.0_@babel+core@7.29.7": {
"integrity": "sha512-TRLHfeQT/q9JXy0cBGOtbfcEWJFF1szjPCVM4AT1HdZyfGj+dQ0r8hHq+08c4iXZLzlsYilzw+bZJ3TahG5g5w==", "integrity": "sha512-TRLHfeQT/q9JXy0cBGOtbfcEWJFF1szjPCVM4AT1HdZyfGj+dQ0r8hHq+08c4iXZLzlsYilzw+bZJ3TahG5g5w==",
"dependencies": [ "dependencies": [
"@lingui/conf", "@lingui/conf",
"@lingui/core" "@lingui/core"
] ]
}, },
"@lingui/vite-plugin@6.8.0_@babel+core@7.29.7_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0_@types+node@26.6.3_jiti@2.7.0": { "@lingui/vite-plugin@6.8.0_@babel+core@7.29.7_vite@8.3.1_@types+node@26.6.3_jiti@2.7.0": {
"integrity": "sha512-pyOGwY8llaXzBBDrbZpAV5a0z7ekz0zJxU+EGXxyqL/gLneqHgLhPCv8uNB+MyIGwrw0PC9CMtCpHVlS3oWguA==", "integrity": "sha512-pyOGwY8llaXzBBDrbZpAV5a0z7ekz0zJxU+EGXxyqL/gLneqHgLhPCv8uNB+MyIGwrw0PC9CMtCpHVlS3oWguA==",
"dependencies": [ "dependencies": [
"@babel/core", "@babel/core",
@@ -869,7 +881,7 @@
"pretty-format@27.5.1" "pretty-format@27.5.1"
] ]
}, },
"@testing-library/jest-dom@7.0.1_@testing-library+dom@10.4.2_vitest@5.0.2__@types+node@26.6.3__@vitest+coverage-v8@5.0.2__jsdom@30.1.1__vite@8.3.1___@types+node@26.6.3___jiti@2.7.0_@vitest+coverage-v8@5.0.2__vitest@5.0.2__@types+node@26.6.3__jsdom@30.1.1__vite@8.3.1___@types+node@26.6.3___jiti@2.7.0": { "@testing-library/jest-dom@7.0.1_@testing-library+dom@10.4.2_vitest@5.0.2_@vitest+coverage-v8@5.0.2": {
"integrity": "sha512-oMDTC3oA+6CXSO2JZnvOI7CA6oVub6kij5ggk9ohwye5slmkwxYDXcPOVxgMw/RQlticjtO0C1RZkR97HgrWMw==", "integrity": "sha512-oMDTC3oA+6CXSO2JZnvOI7CA6oVub6kij5ggk9ohwye5slmkwxYDXcPOVxgMw/RQlticjtO0C1RZkR97HgrWMw==",
"dependencies": [ "dependencies": [
"@adobe/css-tools", "@adobe/css-tools",
@@ -885,7 +897,7 @@
"vitest" "vitest"
] ]
}, },
"@testing-library/react@16.3.3_@testing-library+dom@10.4.2_@types+react@19.3.0_@types+react-dom@19.3.0__@types+react@19.3.0_react@19.3.0_react-dom@19.3.0__react@19.3.0": { "@testing-library/react@16.3.3_@testing-library+dom@10.4.2_@types+react@19.3.0_@types+react-dom@19.3.0_react@19.3.0_react-dom@19.3.0": {
"integrity": "sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==", "integrity": "sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==",
"dependencies": [ "dependencies": [
"@babel/runtime", "@babel/runtime",
@@ -1003,7 +1015,7 @@
"@types/yargs-parser" "@types/yargs-parser"
] ]
}, },
"@typescript-eslint/eslint-plugin@8.70.1_@typescript-eslint+parser@8.70.1__eslint@10.11.0___jiti@2.7.0__typescript@6.0.3_eslint@10.11.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": { "@typescript-eslint/eslint-plugin@8.70.1_@typescript-eslint+parser@8.70.1_eslint@10.11.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==", "integrity": "sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==",
"dependencies": [ "dependencies": [
"@eslint-community/regexpp", "@eslint-community/regexpp",
@@ -1019,7 +1031,7 @@
"typescript" "typescript"
] ]
}, },
"@typescript-eslint/parser@8.70.1_eslint@10.11.0__jiti@2.7.0_typescript@6.0.3": { "@typescript-eslint/parser@8.70.1_eslint@10.11.0_typescript@6.0.3": {
"integrity": "sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw==", "integrity": "sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw==",
"dependencies": [ "dependencies": [
"@typescript-eslint/scope-manager", "@typescript-eslint/scope-manager",
@@ -1053,7 +1065,7 @@
"typescript" "typescript"
] ]
}, },
"@typescript-eslint/type-utils@8.70.1_eslint@10.11.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": { "@typescript-eslint/type-utils@8.70.1_eslint@10.11.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==", "integrity": "sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==",
"dependencies": [ "dependencies": [
"@typescript-eslint/types", "@typescript-eslint/types",
@@ -1083,7 +1095,7 @@
"typescript" "typescript"
] ]
}, },
"@typescript-eslint/utils@8.70.1_eslint@10.11.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": { "@typescript-eslint/utils@8.70.1_eslint@10.11.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==", "integrity": "sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==",
"dependencies": [ "dependencies": [
"@eslint-community/eslint-utils", "@eslint-community/eslint-utils",
@@ -1105,7 +1117,7 @@
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
"deprecated": true "deprecated": true
}, },
"@vitejs/plugin-react-swc@4.3.3_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0_@types+node@26.6.3_jiti@2.7.0": { "@vitejs/plugin-react-swc@4.3.3_vite@8.3.1_@types+node@26.6.3_jiti@2.7.0": {
"integrity": "sha512-bti8ZAcvz4Lh6/e4Uk2k3aa1TiUXbbMsahuqOHvd3MveFTkKDZOA6wQVkpj7J/+tepX/wGfe+lsGh/t24HTXMQ==", "integrity": "sha512-bti8ZAcvz4Lh6/e4Uk2k3aa1TiUXbbMsahuqOHvd3MveFTkKDZOA6wQVkpj7J/+tepX/wGfe+lsGh/t24HTXMQ==",
"dependencies": [ "dependencies": [
"@rolldown/pluginutils", "@rolldown/pluginutils",
@@ -1113,7 +1125,7 @@
"vite" "vite"
] ]
}, },
"@vitest/coverage-v8@5.0.2_vitest@5.0.2_@types+node@26.6.3_jsdom@30.1.1_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0": { "@vitest/coverage-v8@5.0.2_vitest@5.0.2_@types+node@26.6.3_jsdom@30.1.1_vite@8.3.1": {
"integrity": "sha512-3ffHBEi8DOOBLwIGBhOBZbRfYFYWjMUuxZicONdhuFEFEG5AVsMOSrVeuROskqnqpbOmEJwxM2eTVZ9N3a1t+A==", "integrity": "sha512-3ffHBEi8DOOBLwIGBhOBZbRfYFYWjMUuxZicONdhuFEFEG5AVsMOSrVeuROskqnqpbOmEJwxM2eTVZ9N3a1t+A==",
"dependencies": [ "dependencies": [
"@bcoe/v8-coverage", "@bcoe/v8-coverage",
@@ -1136,7 +1148,7 @@
"@vitest/istanbul-lib-coverage" "@vitest/istanbul-lib-coverage"
] ]
}, },
"@vitest/mocker@5.0.2_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0_@types+node@26.6.3_jiti@2.7.0": { "@vitest/mocker@5.0.2_vite@8.3.1_@types+node@26.6.3_jiti@2.7.0": {
"integrity": "sha512-Z5FS00Q1SJHkB35xATsmWGdQ5WA1/0MV3CDjqyv7GavHv1OfOj145MNfHOlHk7QLes21dKFDHr8EO2zvL+9WGA==", "integrity": "sha512-Z5FS00Q1SJHkB35xATsmWGdQ5WA1/0MV3CDjqyv7GavHv1OfOj145MNfHOlHk7QLes21dKFDHr8EO2zvL+9WGA==",
"dependencies": [ "dependencies": [
"@jridgewell/trace-mapping", "@jridgewell/trace-mapping",
@@ -1428,7 +1440,7 @@
"escape-string-regexp@5.0.0": { "escape-string-regexp@5.0.0": {
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==" "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
}, },
"eslint-plugin-react-hooks@7.1.1_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0": { "eslint-plugin-react-hooks@7.1.1_eslint@10.11.0_jiti@2.7.0": {
"integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==",
"dependencies": [ "dependencies": [
"@babel/core", "@babel/core",
@@ -1439,7 +1451,7 @@
"zod-validation-error" "zod-validation-error"
] ]
}, },
"eslint-plugin-react-refresh@0.5.7_eslint@10.11.0__jiti@2.7.0_jiti@2.7.0": { "eslint-plugin-react-refresh@0.5.7_eslint@10.11.0_jiti@2.7.0": {
"integrity": "sha512-XhJSzLljuYD4UjNuFGJu2v7aD3sqNVK12w5/DCUfrNfHWegZo2+TsavNx8MuxMwEquuFAzNbNfXKU0WoZ+YUIg==", "integrity": "sha512-XhJSzLljuYD4UjNuFGJu2v7aD3sqNVK12w5/DCUfrNfHWegZo2+TsavNx8MuxMwEquuFAzNbNfXKU0WoZ+YUIg==",
"dependencies": [ "dependencies": [
"eslint" "eslint"
@@ -2569,7 +2581,7 @@
"vfile" "vfile"
] ]
}, },
"react-router@8.4.0_react@19.3.0_react-dom@19.3.0__react@19.3.0": { "react-router@8.4.0_react@19.3.0_react-dom@19.3.0": {
"integrity": "sha512-JtydAkBvU9UTEe5qNC+POhu+ZBNM7rlKY2IegwXc7Idj7xfRG16x5RZrw3mju+XlqBxfvb2ky9P3jUp9WyWC1g==", "integrity": "sha512-JtydAkBvU9UTEe5qNC+POhu+ZBNM7rlKY2IegwXc7Idj7xfRG16x5RZrw3mju+XlqBxfvb2ky9P3jUp9WyWC1g==",
"dependencies": [ "dependencies": [
"@remix-run/route-pattern", "@remix-run/route-pattern",
@@ -2834,7 +2846,7 @@
"prelude-ls" "prelude-ls"
] ]
}, },
"typescript-eslint@8.70.1_eslint@10.11.0__jiti@2.7.0_typescript@6.0.3_jiti@2.7.0": { "typescript-eslint@8.70.1_eslint@10.11.0_typescript@6.0.3_jiti@2.7.0": {
"integrity": "sha512-AcWG7KDjZ2THNXsgwttMaGmzVi0VFRlFYfqFHYQRbDpF3owuYbuiL8c7UUrd2k8s3PoSfIQrWfrGXfcElrWLYA==", "integrity": "sha512-AcWG7KDjZ2THNXsgwttMaGmzVi0VFRlFYfqFHYQRbDpF3owuYbuiL8c7UUrd2k8s3PoSfIQrWfrGXfcElrWLYA==",
"dependencies": [ "dependencies": [
"@typescript-eslint/eslint-plugin", "@typescript-eslint/eslint-plugin",
@@ -2955,7 +2967,7 @@
], ],
"bin": true "bin": true
}, },
"vitest@5.0.2_@types+node@26.6.3_@vitest+coverage-v8@5.0.2_jsdom@30.1.1_vite@8.3.1__@types+node@26.6.3__jiti@2.7.0": { "vitest@5.0.2_@types+node@26.6.3_@vitest+coverage-v8@5.0.2_jsdom@30.1.1_vite@8.3.1": {
"integrity": "sha512-7MQrx9pDv5aHiUcovIb/70Ys3tgtkUVgCtledvKdCmEO+/1Dicq5ZqoSxOW034m03oqC+oHOKui2dM6qtMLoJg==", "integrity": "sha512-7MQrx9pDv5aHiUcovIb/70Ys3tgtkUVgCtledvKdCmEO+/1Dicq5ZqoSxOW034m03oqC+oHOKui2dM6qtMLoJg==",
"dependencies": [ "dependencies": [
"@types/chai", "@types/chai",
@@ -3071,9 +3083,12 @@
"npm:@eslint/js@^10.0.1", "npm:@eslint/js@^10.0.1",
"npm:@fontsource-variable/fraunces@5.3.0", "npm:@fontsource-variable/fraunces@5.3.0",
"npm:@fontsource-variable/jetbrains-mono@5.3.0", "npm:@fontsource-variable/jetbrains-mono@5.3.0",
"npm:@fontsource-variable/libre-franklin@5.3.0",
"npm:@fontsource-variable/newsreader@5.3.0",
"npm:@fontsource-variable/saira@5.3.0", "npm:@fontsource-variable/saira@5.3.0",
"npm:@fontsource-variable/space-grotesk@5.3.0", "npm:@fontsource-variable/space-grotesk@5.3.0",
"npm:@fontsource/instrument-serif@5.3.0", "npm:@fontsource/instrument-serif@5.3.0",
"npm:@fontsource/manufacturing-consent@5.3.0",
"npm:@lingui/cli@6.8.0", "npm:@lingui/cli@6.8.0",
"npm:@lingui/conf@6.8.0", "npm:@lingui/conf@6.8.0",
"npm:@lingui/core@6.8.0", "npm:@lingui/core@6.8.0",

View File

@@ -14,7 +14,10 @@
"@fontsource-variable/jetbrains-mono": "5.3.0", "@fontsource-variable/jetbrains-mono": "5.3.0",
"@fontsource-variable/saira": "5.3.0", "@fontsource-variable/saira": "5.3.0",
"@fontsource-variable/space-grotesk": "5.3.0", "@fontsource-variable/space-grotesk": "5.3.0",
"@fontsource-variable/libre-franklin": "5.3.0",
"@fontsource-variable/newsreader": "5.3.0",
"@fontsource/instrument-serif": "5.3.0", "@fontsource/instrument-serif": "5.3.0",
"@fontsource/manufacturing-consent": "5.3.0",
"@lingui/cli": "6.8.0", "@lingui/cli": "6.8.0",
"@lingui/conf": "6.8.0", "@lingui/conf": "6.8.0",
"@lingui/core": "6.8.0", "@lingui/core": "6.8.0",

View File

@@ -62,7 +62,8 @@
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
} }
/* nyt */ /* nyt: the headlines' serif, the text's (opsz as well as wght), the
interface's sans, and the nameplate's blackletter */
@font-face { @font-face {
font-family: 'Instrument Serif'; font-family: 'Instrument Serif';
font-style: normal; font-style: normal;
@@ -99,6 +100,96 @@
format('woff2'); format('woff2');
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
} }
@font-face {
font-family: 'Newsreader Variable';
font-style: normal;
font-display: swap;
font-weight: 200 800;
src: url(@fontsource-variable/newsreader/files/newsreader-latin-ext-opsz-normal.woff2)
format('woff2-variations');
unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
font-family: 'Newsreader Variable';
font-style: normal;
font-display: swap;
font-weight: 200 800;
src: url(@fontsource-variable/newsreader/files/newsreader-latin-opsz-normal.woff2)
format('woff2-variations');
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
font-family: 'Newsreader Variable';
font-style: italic;
font-display: swap;
font-weight: 200 800;
src: url(@fontsource-variable/newsreader/files/newsreader-latin-ext-opsz-italic.woff2)
format('woff2-variations');
unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
font-family: 'Newsreader Variable';
font-style: italic;
font-display: swap;
font-weight: 200 800;
src: url(@fontsource-variable/newsreader/files/newsreader-latin-opsz-italic.woff2)
format('woff2-variations');
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
font-family: 'Libre Franklin Variable';
font-style: normal;
font-display: swap;
font-weight: 100 900;
src: url(@fontsource-variable/libre-franklin/files/libre-franklin-latin-ext-wght-normal.woff2)
format('woff2-variations');
unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
font-family: 'Libre Franklin Variable';
font-style: normal;
font-display: swap;
font-weight: 100 900;
src: url(@fontsource-variable/libre-franklin/files/libre-franklin-latin-wght-normal.woff2)
format('woff2-variations');
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
font-family: 'Libre Franklin Variable';
font-style: italic;
font-display: swap;
font-weight: 100 900;
src: url(@fontsource-variable/libre-franklin/files/libre-franklin-latin-ext-wght-italic.woff2)
format('woff2-variations');
unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
font-family: 'Libre Franklin Variable';
font-style: italic;
font-display: swap;
font-weight: 100 900;
src: url(@fontsource-variable/libre-franklin/files/libre-franklin-latin-wght-italic.woff2)
format('woff2-variations');
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
font-family: 'Manufacturing Consent';
font-style: normal;
font-display: swap;
font-weight: 400;
src: url(@fontsource/manufacturing-consent/files/manufacturing-consent-latin-ext-400-normal.woff2)
format('woff2');
unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
font-family: 'Manufacturing Consent';
font-style: normal;
font-display: swap;
font-weight: 400;
src: url(@fontsource/manufacturing-consent/files/manufacturing-consent-latin-400-normal.woff2)
format('woff2');
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
/* kanagawa */ /* kanagawa */
@font-face { @font-face {

View File

@@ -1,7 +1,9 @@
/* ══════════════════════════════════════════════════════════════════════ /* ══════════════════════════════════════════════════════════════════════
"Old Grey Lady" theme — New York Times editorial styling. "Old Grey Lady" theme — New York Times editorial styling.
Serif typography, paper-and-ink palette, hairline rules, sharp corners, Four faces, each with one job: a blackletter nameplate, a condensed display
restrained NYT-blue links, inverted ink-on-paper primary buttons. serif for headlines, a news serif for prose, and a Franklin gothic for
everything the interface says.
Paper-and-ink palette, ink rules over hairlines, square pictures.
Light scheme → newsprint (white page, black ink). Light scheme → newsprint (white page, black ink).
Dark scheme → NYT night mode (near-black page, warm-grey ink). Dark scheme → NYT night mode (near-black page, warm-grey ink).
@@ -9,33 +11,26 @@
/* ── Palette (light-dark: see tokens.css) ─────────────────────────── */ /* ── Palette (light-dark: see tokens.css) ─────────────────────────── */
:root[data-style="nyt"] { :root[data-style="nyt"] {
/* Square corners everywhere App.css uses the radius scale. */ /* Pictures, cards and sheets are square; a control keeps the 3px of a
newspaper site's buttons (see "Controls" below). */
--radius-xs: 0; --radius-xs: 0;
--radius-sm: 0; --radius-sm: 0;
--radius-md: 0; --radius-md: 0;
--radius-lg: 0; --radius-lg: 0;
--radius-xl: 0; --radius-xl: 0;
--radius-full: 0; --radius-full: 0;
--nyt-control-radius: 3px;
/* Hairlines, not strokes — controls match the rules between stories. */ /* Hairlines, not strokes — controls match the rules between stories. */
--border-width: 1px; --border-width: 1px;
/* Instrument Serif is a condensed display face: at the shared scale its
small sizes read a step smaller than other themes', so lift the low end. */
--text-2xs: 0.78rem;
--text-xs: 0.86rem;
--text-sm: 0.94rem;
--text-md: 1rem;
--text-base: 1.06rem;
--text-lg: 1.2rem;
--color-text: light-dark(#121212, #e6e6e6); --color-text: light-dark(#121212, #e6e6e6);
--color-text-secondary: light-dark(#5a5a5a, #ababab); --color-text-secondary: light-dark(#5a5a5a, #ababab);
--color-text-muted: light-dark(#767676, #8a8a8a); --color-text-muted: light-dark(#727272, #8a8a8a);
--color-on-accent: light-dark(#ffffff, #121212); --color-on-accent: light-dark(#ffffff, #121212);
--color-bg: light-dark(#ffffff, #121212); --color-bg: light-dark(#ffffff, #121212);
--color-surface: light-dark(#ffffff, #1e1e1e); --color-surface: light-dark(#ffffff, #1e1e1e);
--color-border: light-dark(#e2e2e2, #3a3a3a); --color-border: light-dark(#dfdfdf, #3a3a3a);
--color-border-subtle: light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.12)); --color-border-subtle: light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.12));
--color-accent: light-dark(#121212, #ececec); --color-accent: light-dark(#121212, #ececec);
--color-accent-hover: light-dark(#000000, #ffffff); --color-accent-hover: light-dark(#000000, #ffffff);
@@ -48,41 +43,115 @@
--color-success-bg: light-dark(#e6f4ea, #122a14); --color-success-bg: light-dark(#e6f4ea, #122a14);
--color-success-hover: light-dark(#136a2c, #95d699); --color-success-hover: light-dark(#136a2c, #95d699);
--color-overlay: light-dark(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.6)); --color-overlay: light-dark(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.6));
--color-header-user-bg: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.06)); /* The header's controls sit on the page; only hovering one tints it. */
--color-header-user-bg-hover: light-dark(rgba(0, 0, 0, 0.09), rgba(255, 255, 255, 0.12)); --color-header-user-bg: transparent;
--color-option-bg: light-dark(#f2f2f2, #181818); --color-header-user-bg-hover: light-dark(rgba(0, 0, 0, 0.06), rgba(255, 255, 255, 0.1));
--color-option-border: light-dark(#e2e2e2, #3a3a3a); --color-option-bg: light-dark(#f4f4f4, #181818);
--color-option-border: light-dark(#dfdfdf, #3a3a3a);
/* The masthead is printed on the page, in both schemes. */
--header-bg: var(--color-bg);
/* Section tabs read as a paper's section list: sentence case, no pill. */
--tab-text-transform: none;
--tab-tracking: 0;
} }
/* ══════════════════════════════════════════════════════════════════════ /* ══════════════════════════════════════════════════════════════════════
Structural overrides Typography
══════════════════════════════════════════════════════════════════════ */ ══════════════════════════════════════════════════════════════════════ */
/* ── Typography — one serif everywhere ───────────────────────────── */ /* What is read is serif, and it is the default; what is operated is sans
(--font-ui, the list below). */
[data-style="nyt"] { [data-style="nyt"] {
--font-body: "Instrument Serif", Georgia, "Times New Roman", serif; --font-body: "Newsreader Variable", Georgia, "Times New Roman", serif;
--font-display: "Instrument Serif", Georgia, "Times New Roman", serif; --font-display: "Instrument Serif", Georgia, "Times New Roman", serif;
--font-ui: "Libre Franklin Variable", "Franklin Gothic Medium", "Helvetica Neue", Arial,
sans-serif;
--font-nameplate: "Manufacturing Consent", "Old English Text MT", var(--font-display);
font-family: var(--font-body); font-family: var(--font-body);
font-weight: 400; font-weight: 400;
line-height: 1.5; line-height: 1.5;
/* Single weight, no small caps: never let the browser fake either — a
smeared faux bold or spindly faux small caps give the typeface away.
Hierarchy comes from size, italics and capitals instead. */
font-synthesis: none;
/* Hairline icons, drawn in ink like the rules. */ /* Hairline icons, drawn in ink like the rules. */
--nav-icon-stroke: 1.25; --nav-icon-stroke: 1.25;
} }
/* This theme squares everything off — opt the FAB into a square too (it keeps /* ── The interface's sans — controls, navigation, labels, datelines ── */
its animations; only the corners change). */ [data-style="nyt"] :is(
[data-style="nyt"] .dump-fab, button,
[data-style="nyt"] .chat-fab { input,
--fab-radius: 0; select,
label,
.app-header-nav,
.app-header-status,
.feed-sort,
.profile-tabs,
.search-tabs,
.playlist-sort-tabs,
.category-switcher-menu,
.user-menu-dropdown,
.user-list-popover,
.dump-card-meta,
.dump-card-vote,
.playlist-card-meta,
.journal-card-meta,
.category-link,
.dump-op,
.dump-actions,
.dump-edited-label,
.rich-content-badge,
.rich-content-url,
.comment-section-title,
.related-section-title,
.comment-meta,
.comment-actions,
.comment-form-actions,
.notification-time,
.notifications-unread-pill,
.notification-badge,
.page-group-label,
.form-page-eyebrow,
.form-hint,
.form-field,
.profile-role-chip,
.profile-invited-by,
.profile-email-display,
.profile-section-header,
.profile-appearance-row,
.playlist-badge,
.dump-card-private-badge,
.avatar-initials,
.thumb-placeholder-initials,
.feed-end,
.empty-state,
.tooltip,
.dump-steps,
.auth-card-footer,
.fdz__hint,
.fdz__limit,
.fdz__file-meta,
.chat-message-meta,
.chat-day-sep,
.chat-status,
.chat-typing,
.chat-reply-bar,
.chat-reply-ref,
.audio-player-time,
.global-player-position,
.global-player-status,
.global-player-subtitle
) {
font-family: var(--font-ui);
} }
/* ── Headlines — display serif ───────────────────────────────────── */ /* A note is written in the face it will be read in. */
[data-style="nyt"] textarea {
font-family: var(--font-body);
font-size: var(--text-base);
}
/* ── Headlines ───────────────────────────────────────────────────── */
[data-style="nyt"] h1, [data-style="nyt"] h1,
[data-style="nyt"] .app-header-brand,
[data-style="nyt"] .dump-title, [data-style="nyt"] .dump-title,
[data-style="nyt"] .dump-card-title, [data-style="nyt"] .dump-card-title,
[data-style="nyt"] .playlist-card-title, [data-style="nyt"] .playlist-card-title,
@@ -91,35 +160,69 @@
[data-style="nyt"] .form-page-title, [data-style="nyt"] .form-page-title,
[data-style="nyt"] .auth-card-title, [data-style="nyt"] .auth-card-title,
[data-style="nyt"] .page-title, [data-style="nyt"] .page-title,
[data-style="nyt"] .dump-create-title { [data-style="nyt"] .profile-username,
[data-style="nyt"] .dump-create-title,
[data-style="nyt"] .rich-content-title,
[data-style="nyt"] .modal-title {
font-family: var(--font-display); font-family: var(--font-display);
font-weight: 400; /* Instrument Serif is single-weight; size carries it */ /* Instrument Serif has one weight: size carries the hierarchy, and the
browser is kept from smearing a bold out of it. */
font-weight: 400;
font-synthesis: none;
letter-spacing: 0; letter-spacing: 0;
line-height: 1.14; line-height: 1.12;
} }
/* A confident broadsheet type scale */ /* A broadsheet's scale. The face is condensed and runs small, so each step
[data-style="nyt"] .app-header-brand { is set larger than its rem reads. */
font-size: 1.45rem;
letter-spacing: 0;
}
[data-style="nyt"] .dump-title { [data-style="nyt"] .dump-title {
font-size: 2.1rem; font-size: clamp(2.2rem, 1.3rem + 3.4vw, 3.2rem);
line-height: 1.04;
} }
[data-style="nyt"] .playlist-detail-title { [data-style="nyt"] .playlist-detail-title {
font-size: 1.9rem; font-size: clamp(2rem, 1.3rem + 2.6vw, 2.75rem);
line-height: 1.06;
}
[data-style="nyt"] .page-title,
[data-style="nyt"] .profile-username,
[data-style="nyt"] .form-page-title,
[data-style="nyt"] .auth-card-title {
font-size: 2.5rem;
line-height: 1.05;
}
/* The base title breaks anywhere, which lets its text be squeezed under the
width of a word: here a word stays whole unless it is wider than the line. */
[data-style="nyt"] .page-title {
overflow-wrap: break-word;
} }
[data-style="nyt"] .dump-card-title, [data-style="nyt"] .dump-card-title,
[data-style="nyt"] .playlist-card-title { [data-style="nyt"] .playlist-card-title {
font-size: 1.2rem; font-size: 1.5rem;
line-height: 1.2; line-height: 1.12;
}
[data-style="nyt"] .rich-content-title {
font-size: 1.3rem;
line-height: 1.15;
}
[data-style="nyt"] .modal-title {
font-size: 1.5rem;
} }
[data-style="nyt"] .journal-card-title { [data-style="nyt"] .journal-card-title {
font-size: 1.15rem; font-size: 1.35rem;
line-height: 1.18; line-height: 1.1;
} }
[data-style="nyt"] .journal-card--feature .journal-card-title { [data-style="nyt"] .journal-card--feature .journal-card-title {
font-size: 1.6rem; font-size: 2.2rem;
line-height: 1.04;
}
/* The base scale sets these three smaller than a card's title; in this face
they need the same lift as the rest. */
[data-style="nyt"] .journal-card--square.journal-card--image .journal-card-title,
[data-style="nyt"] .journal-card--text .journal-card-title {
font-size: 1.25rem;
}
[data-style="nyt"] .journal-card--quote .journal-card-title {
font-size: 1.1rem;
} }
/* Headline links underline on hover (ink stays ink). */ /* Headline links underline on hover (ink stays ink). */
@@ -128,7 +231,8 @@
[data-style="nyt"] .journal-card-title:hover { [data-style="nyt"] .journal-card-title:hover {
color: var(--color-text); color: var(--color-text);
text-decoration: underline; text-decoration: underline;
text-underline-offset: 2px; text-decoration-thickness: 1px;
text-underline-offset: 3px;
} }
/* …except over a photo, where the headline is white on a dark scrim: ink /* …except over a photo, where the headline is white on a dark scrim: ink
there goes black-on-black in the light scheme. */ there goes black-on-black in the light scheme. */
@@ -150,28 +254,264 @@
text-decoration: none; text-decoration: none;
} }
/* ── Kickers — section labels & badges in spaced capitals ───────── */ /* ── Summaries — the grey paragraph under a headline ─────────────── */
[data-style="nyt"] .comment-section-title, [data-style="nyt"] .dump-card-comment {
[data-style="nyt"] .related-section-title, color: var(--color-text-secondary);
[data-style="nyt"] .form-page-eyebrow, font-size: var(--text-base);
[data-style="nyt"] .playlist-badge, line-height: 1.4;
[data-style="nyt"] .dump-card-private-badge, margin-top: 0.2rem;
[data-style="nyt"] .page-group-label,
[data-style="nyt"] .follow-btn {
text-transform: uppercase;
letter-spacing: 0.08em;
} }
/* ── Datelines — the serif's real italic, in muted ink ────────────── */ [data-style="nyt"] .rich-content-description {
font-size: var(--text-md);
line-height: 1.4;
}
/* ── Section labels — heavy sans capitals under an ink rule ──────── */
[data-style="nyt"] .comment-section-title,
[data-style="nyt"] .related-section-title,
[data-style="nyt"] .page-group-label,
[data-style="nyt"] .form-page-eyebrow {
color: var(--color-text);
font-size: var(--text-2xs);
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
}
[data-style="nyt"] .page-group-label {
border-top: 1px solid var(--color-text);
padding-top: 0.5rem;
}
/* These two sections already open on a rule: it turns to ink and the label
moves up under it. */
[data-style="nyt"] .comment-section,
[data-style="nyt"] .related-section {
border-top-color: var(--color-text);
padding-top: 0.5rem;
}
/* The label's own trailing hairline would double the rule above it. */
[data-style="nyt"] .page-group-label::after {
content: none;
}
[data-style="nyt"] .profile-section-header {
border-bottom: none;
border-top: 1px solid var(--color-text);
padding: 0.5rem 0 0;
}
[data-style="nyt"] .profile-section-title {
font-family: var(--font-ui);
font-size: var(--text-2xs);
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
}
/* ── Kickers — a story's categories, and the small badges ────────── */
[data-style="nyt"] .category-link,
[data-style="nyt"] .playlist-badge,
[data-style="nyt"] .dump-card-private-badge,
[data-style="nyt"] .profile-role-chip,
[data-style="nyt"] .rich-content-badge {
font-size: 0.66rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
}
[data-style="nyt"] .category-link {
padding: 0;
background: none;
color: var(--color-text);
}
[data-style="nyt"] .category-link:hover {
background: none;
text-decoration: underline;
text-underline-offset: 2px;
}
[data-style="nyt"] .dump-card-categories {
gap: 0.15rem 0.75rem;
}
/* ── Datelines and bylines — small sans, the name in ink ─────────── */
[data-style="nyt"] .dump-card-date, [data-style="nyt"] .dump-card-date,
[data-style="nyt"] .dump-card-comment-count,
[data-style="nyt"] .playlist-card-meta,
[data-style="nyt"] .journal-card-meta, [data-style="nyt"] .journal-card-meta,
[data-style="nyt"] .comment-time, [data-style="nyt"] .comment-time,
[data-style="nyt"] .notification-time, [data-style="nyt"] .notification-time,
[data-style="nyt"] .dump-edited-label { [data-style="nyt"] .dump-edited-label {
text-transform: none; font-size: 0.72rem;
letter-spacing: 0; font-style: normal;
font-style: italic; letter-spacing: 0.01em;
} }
[data-style="nyt"] .dump-op {
opacity: 1;
color: var(--color-text-muted);
font-size: var(--text-xs);
margin-top: 0.6rem;
}
[data-style="nyt"] .dump-op-link,
[data-style="nyt"] .comment-author {
color: var(--color-text);
font-size: var(--text-xs);
font-weight: 700;
}
[data-style="nyt"] .dump-actions a {
font-size: var(--text-xs);
font-weight: 500;
}
[data-style="nyt"] .vote-btn {
font-size: var(--text-xs);
font-weight: 600;
font-variant-numeric: tabular-nums;
}
/* ══════════════════════════════════════════════════════════════════════
Masthead
══════════════════════════════════════════════════════════════════════ */
/* The nameplate: the site's name in blackletter, its emoji beside it at the
size of a printer's mark, the two on one baseline. Centring them put the
emoji against the middle of the name's line, which is above the middle of
its letters. The face has one weight; the base rule's bold would be faked. */
[data-style="nyt"] .app-header-brand {
display: inline-flex;
align-items: baseline;
gap: 0.15em;
font-size: 1.25rem;
font-weight: 400;
letter-spacing: 0;
line-height: 1;
padding-block: 0.7rem 0.6rem;
white-space: nowrap;
}
[data-style="nyt"] .app-header-brand-name {
font-family: var(--font-nameplate);
font-size: 1.8rem;
/* An emoji hangs a quarter of its height under the baseline, so on a shared
baseline its middle is a little under the middle of the lowercase: the
name comes down to meet it. */
translate: 0 0.04em;
}
[data-style="nyt"] .app-header {
border-bottom: 1px solid var(--color-text);
}
/* Where there is room the nameplate takes the middle of a row of its own,
the buttons beside it, and what the page puts in the header (a feed's
sections, the open search field) runs underneath between a hairline and a
double rule, as a front page's section list does. A page that puts nothing
there keeps the one row and a single rule. */
@media (min-width: 860px) {
[data-style="nyt"] .app-header--has-center {
grid-template-columns: 1fr auto 1fr;
column-gap: 1rem;
row-gap: 0;
}
[data-style="nyt"] .app-header-brand {
grid-area: 1 / 2;
justify-self: center;
font-size: 1.5rem;
padding-block: 1.1rem 0.9rem;
}
[data-style="nyt"] .app-header-brand-name {
font-size: 2.9rem;
}
[data-style="nyt"] .app-header--has-center .app-header-nav {
grid-area: 1 / 3;
}
[data-style="nyt"] .app-header-center {
grid-area: 2 / 1 / auto / -1;
border-top: 1px solid var(--color-border);
padding-block: 0.3rem;
}
[data-style="nyt"] .app-header-center:not(:has(.header-center-slot, .search-bar--expanded)) {
display: none;
}
[data-style="nyt"] .app-header:has(.header-center-slot, .search-bar--expanded) {
border-bottom: 3px double var(--color-text);
}
}
[data-style="nyt"] .header-search-below {
border-bottom: 1px solid var(--color-border);
}
/* The header's links and buttons: small sans, the weight of a section list. */
[data-style="nyt"] .app-header-nav button,
[data-style="nyt"] .app-header-user {
font-size: var(--text-xs);
font-weight: 600;
}
[data-style="nyt"] .app-header-nav button:not(.btn-primary) {
border-color: transparent;
}
[data-style="nyt"] .app-header--guest .app-header-nav > button:last-child {
border-color: var(--color-text);
}
/* ── Section tabs — a word, underlined in ink when it is the open one ── */
[data-style="nyt"] .feed-sort {
gap: 0.25rem;
}
[data-style="nyt"] .feed-sort-btn {
border: none;
border-bottom: 2px solid transparent;
border-radius: 0;
background: none;
color: var(--color-text-secondary);
padding: 0.3rem 0.1rem 0.2rem;
margin: 0 0.5rem;
font-size: var(--text-xs);
font-weight: 500;
}
[data-style="nyt"] .feed-sort-btn:hover {
background: none;
color: var(--color-text);
}
[data-style="nyt"] .feed-sort-btn.active {
background: none;
color: var(--color-text);
font-weight: 700;
border-bottom-color: var(--color-text);
}
/* ══════════════════════════════════════════════════════════════════════
Controls
══════════════════════════════════════════════════════════════════════ */
[data-style="nyt"] :is(
.btn,
.btn-primary,
.btn-sm,
.vote-btn,
.follow-btn,
.invite-btn,
.btn-add-playlist,
.new-item-toggle,
.app-header-nav button,
.app-header-user,
input,
select,
textarea
) {
border-radius: var(--nyt-control-radius);
}
[data-style="nyt"] :is(.btn, .btn-primary, .btn-sm, .follow-btn, .invite-btn) {
font-weight: 600;
letter-spacing: 0.01em;
}
/* This theme squares everything off — opt the FAB into a square too (it keeps
its animations; only the corners change). */
[data-style="nyt"] .dump-fab,
[data-style="nyt"] .chat-fab {
--fab-radius: 0;
}
/* ══════════════════════════════════════════════════════════════════════
Structural overrides
══════════════════════════════════════════════════════════════════════ */
/* ── Lead note — a standfirst with an editorial drop cap ─────────── */ /* ── Lead note — a standfirst with an editorial drop cap ─────────── */
[data-style="nyt"] .dump-comment { [data-style="nyt"] .dump-comment {
@@ -179,24 +519,26 @@
padding: 0; padding: 0;
margin: 0.35rem 0 0; margin: 0.35rem 0 0;
opacity: 1; opacity: 1;
color: var(--color-text-secondary); color: var(--color-text);
font-size: 1.2rem; font-size: 1.2rem;
}
[data-style="nyt"] .dump-comment,
[data-style="nyt"] .dump-comment p {
line-height: 1.6; line-height: 1.6;
} }
[data-style="nyt"] .dump-comment p:first-of-type::first-letter { [data-style="nyt"] .dump-comment p:first-of-type::first-letter {
float: left; float: left;
font-family: var(--font-display); font-family: var(--font-display);
font-weight: 400; font-weight: 400;
font-size: 3.4rem; font-size: 4.4rem;
line-height: 0.78; line-height: 0.78;
padding: 0.12em 0.14em 0 0; padding: 0.08em 0.1em 0 0;
color: var(--color-text); color: var(--color-text);
} }
/* ── Generous editorial leading for the lead note; comments stay tight ── */ /* Comments are read too: serif, a step over the labels around them. */
[data-style="nyt"] .dump-comment, [data-style="nyt"] .comment-body {
[data-style="nyt"] .dump-comment p { font-size: var(--text-base);
line-height: 1.7;
} }
/* Tight, ruled comment thread — small padding, snug leading, no extra gaps. */ /* Tight, ruled comment thread — small padding, snug leading, no extra gaps. */
@@ -266,27 +608,29 @@
} }
/* Journal view: a section-front grid of stories divided by hairline rules, /* Journal view: a section-front grid of stories divided by hairline rules,
not boxed cards. Close the gaps and rule each cell on its top & left edge so not boxed cards. Every cell draws a rule on all four of its sides, as a
the rules read as continuous row + column delimiters (à la nytimes.com). 1px shadow lying in a 1px gap: two neighbours' shadows fall on the same
The outermost rules are masked off (below) so there's no frame around the pixels and read as one rule, and a cell beside an empty track (the last
block — CSS grid can't target a first column or a last row, hence the mask. */ row, when the stories don't fill it) still closes on its own. A rule on the
top and left edges only left such a cell open on the right, and the row
above it unruled over the empty tracks. The rules around the outside of the
block are masked off (below), so there is no frame. */
/* How far a photo holds off the rules. It sits on the feed rather than on the /* How far a photo holds off the rules. It sits on the feed rather than on the
grid because the break between two grids is cut to the same measure, and a grid because the break between two grids is cut to the same measure, and a
property declared on the grid wouldn't reach it. */ property declared on the grid wouldn't reach it. */
[data-style="nyt"] .journal-feed { [data-style="nyt"] .journal-feed {
--journal-art-inset: 0.55rem; --journal-art-inset: 0.55rem;
--journal-rule: 0 0 0 1px var(--color-border);
} }
[data-style="nyt"] .journal-grid { [data-style="nyt"] .journal-grid {
gap: 0; gap: 1px;
position: relative; position: relative;
} }
[data-style="nyt"] .journal-card { [data-style="nyt"] .journal-card {
background: transparent; background: transparent;
border: none; border: none;
border-top: 1px solid var(--color-border); box-shadow: var(--journal-rule);
border-left: 1px solid var(--color-border);
box-shadow: none;
} }
/* Rules run to the edges of their cell, but a photo holds off them by /* Rules run to the edges of their cell, but a photo holds off them by
@@ -298,14 +642,14 @@
One page-coloured frame, laid over the grid's own edge and as thick as that One page-coloured frame, laid over the grid's own edge and as thick as that
inset, cuts all of them back to the art at once. It takes the outer rules inset, cuts all of them back to the art at once. It takes the outer rules
(the first row's top, the first column's left) with it, which is the same with it, which sit one pixel outside the grid, hence the negative inset.
frameless block the two strips it replaces were after. One frame per grid One frame per grid means one per sub-block, so every block reads frameless,
means one per sub-block, so every block reads frameless, not just the feed. */ not just the feed. */
[data-style="nyt"] .journal-grid::before { [data-style="nyt"] .journal-grid::before {
content: ""; content: "";
position: absolute; position: absolute;
inset: 0; inset: -1px;
border: var(--journal-art-inset) solid var(--color-bg); border: calc(var(--journal-art-inset) + 1px) solid var(--color-bg);
z-index: 1; z-index: 1;
pointer-events: none; pointer-events: none;
} }
@@ -327,9 +671,8 @@
margin: var(--journal-break-space) var(--journal-art-inset); margin: var(--journal-break-space) var(--journal-art-inset);
} }
[data-style="nyt"] .journal-card:hover { [data-style="nyt"] .journal-card:hover {
border-color: var(--color-border);
transform: none; transform: none;
box-shadow: none; box-shadow: var(--journal-rule);
} }
/* Inset content from the rules so the grid breathes — photos get a margin /* Inset content from the rules so the grid breathes — photos get a margin
@@ -346,15 +689,6 @@
padding: var(--journal-quote-pad); padding: var(--journal-quote-pad);
} }
/* ── App header — crisp masthead rule ────────────────────────────── */
[data-style="nyt"] .app-header {
border-bottom: 2px solid var(--color-text);
}
[data-style="nyt"] .header-search-below {
border-bottom: 1px solid var(--color-border);
}
/* ── Primary button — inverted ink-on-paper (auto-flips per scheme) ─ */ /* ── Primary button — inverted ink-on-paper (auto-flips per scheme) ─ */
[data-style="nyt"] .btn-primary { [data-style="nyt"] .btn-primary {
background: var(--color-text); background: var(--color-text);
@@ -420,27 +754,6 @@
notification badge, brand-provider cues). Everything else is ink & paper. notification badge, brand-provider cues). Everything else is ink & paper.
══════════════════════════════════════════════════════════════════════ */ ══════════════════════════════════════════════════════════════════════ */
/* Feed sort — section-front tabs: an underline, not a filled chip. */
[data-style="nyt"] .feed-sort-btn {
border: none;
border-bottom: 2px solid transparent;
background: none;
color: var(--color-text-muted);
padding: 0.2rem 0.1rem;
margin: 0 0.3rem;
font-weight: 400;
letter-spacing: 0.1em; /* condensed caps need more air than the default */
}
[data-style="nyt"] .feed-sort-btn:hover {
background: none;
color: var(--color-text);
}
[data-style="nyt"] .feed-sort-btn.active {
background: none;
color: var(--color-text);
border-bottom-color: var(--color-text);
}
/* Square off the remaining pills & chips. */ /* Square off the remaining pills & chips. */
[data-style="nyt"] .playlist-badge, [data-style="nyt"] .playlist-badge,
[data-style="nyt"] .dump-card-private-badge, [data-style="nyt"] .dump-card-private-badge,