Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 0 additions & 61 deletions web/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,6 @@
.journal-lockup,
.journal-featured-meta,
.journal-archive-label,
.journal-list-number,
.journal-list-meta,
.journal-index-footer,
.journal-back-link,
Expand Down Expand Up @@ -1239,14 +1238,6 @@
rgba(20, 20, 20, 0.035) 15px
);
}
.journal-index-thumb span {
position: relative;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-muted);
}
.journal-index-thumb img {
position: relative;
z-index: 1;
Expand All @@ -1265,17 +1256,6 @@
border: 1px solid var(--rule);
background: var(--cream-2);
}
.journal-cover-slot::before {
content: none;
}
.journal-cover-slot span {
position: relative;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-muted);
}
.journal-cover-slot img {
position: relative;
z-index: 1;
Expand Down Expand Up @@ -1318,9 +1298,6 @@
.journal-post-list a .pf-ghost {
right: 0;
}
.journal-list-number {
color: var(--ink-faint);
}
.journal-list-body {
display: grid;
gap: 4px;
Expand Down Expand Up @@ -1455,10 +1432,6 @@
overflow: visible;
text-align: center;
}
.journal-hero-cover .journal-cover-slot::before {
content: none;
display: none;
}
.journal-hero-cover .journal-cover-slot img {
display: block;
width: auto;
Expand Down Expand Up @@ -1635,27 +1608,6 @@
border: 0;
background: var(--rule);
}
.journal-figure {
margin: 36px 0;
}
.journal-figure-wide {
width: min(82vw, 1312px);
margin-left: 50%;
transform: translateX(-50%);
}
.journal-figure-full {
width: 100vw;
margin-left: 50%;
transform: translateX(-50%);
}
.journal-figure img,
.journal-figure video {
width: 100%;
height: auto;
display: block;
border: 1px solid var(--rule);
background: var(--cream-2);
}
.journal-lightbox {
position: fixed;
inset: 0;
Expand Down Expand Up @@ -1704,13 +1656,6 @@
text-transform: uppercase;
cursor: pointer;
}
.journal-figure figcaption {
margin-top: 10px;
font-family: var(--mono);
font-size: var(--pf-fs-mono);
letter-spacing: 0.06em;
color: var(--ink-muted);
}
.journal-end-mark {
margin-top: 80px;
display: flex;
Expand Down Expand Up @@ -1995,12 +1940,6 @@
margin-right: 12px;
color: var(--pf-ink-muted);
}
.journal-figure-wide,
.journal-figure-full {
width: 100%;
margin-left: 0;
transform: none;
}
.journal-md-figure,
.journal-md-figure:has(+ .journal-md-figure),
.journal-md-figure:has(+ .journal-md-figure) + .journal-md-figure {
Expand Down
67 changes: 66 additions & 1 deletion web/src/components/journal/journalCss.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
// ("2026년 9월 29일" is 122px) and 17 of the 37 English ones ("SEPTEMBER 29,
// 2026" is 159px) broke onto a second line, leaving 일 or 2026 alone under it.

import { readFileSync } from "node:fs";
import { readdirSync, readFileSync } from "node:fs";
import path from "node:path";
import { createElement } from "react";
import { render } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import JournalImage from "./JournalImage";

const css = readFileSync(path.resolve(__dirname, "../../app/globals.css"), "utf8");

Expand Down Expand Up @@ -240,3 +243,65 @@ describe("journal footers", () => {
expect(sides(phone, ".journal-index-footer")).toEqual([0, 0]);
});
});

// Rules that matched nothing on a journal page. No component and none of the
// 74 posts wrote .journal-list-number, .journal-figure, -wide or -full (the
// posts' figures are journal-md-figure and journal-figure-row). The two cover
// slots hold a JournalImage, and the span rules inside them waited for a span
// it never draws. The cover slot's ::before was switched off twice, and no
// rule ever switched it on. On 27 October that was 61 lines of globals.css.

/** Every .ts and .tsx under src/ that is not a test, and every post. */
function written() {
const out: string[] = [];
const walk = (dir: string) => {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const at = path.join(dir, entry.name);
if (entry.isDirectory()) walk(at);
else if (/\.(tsx?|mdx?)$/.test(entry.name) && !/\.test\./.test(entry.name)) out.push(readFileSync(at, "utf8"));
}
};
walk(path.resolve(__dirname, "../.."));
walk(path.resolve(__dirname, "../../../content/journal"));
return out.join("\n");
}

describe("journal rules", () => {
const all = rules(css);
const selectors = all.flatMap((r) => r.selectors);

it("name only classes a journal component or post writes", () => {
const source = written();
const named = new Set(selectors.flatMap((s) => [...s.matchAll(/\.(journal-[\w-]+)/g)].map((m) => m[1])));
const unwritten = [...named].filter((c) => !new RegExp(`(^|[^\\w-])${c}(?![\\w-])`).test(source));
expect(named.size).toBeGreaterThan(30);
expect(unwritten).toEqual([]);
});

it("style nothing in a cover slot but what JournalImage draws", () => {
const { container } = render(createElement(JournalImage, { src: "/journal/good-but/funding-success.png" }));
const drawn = new Set([...container.querySelectorAll("*")].map((el) => el.tagName.toLowerCase()));
const inSlot = selectors
.map((s) => s.match(/\.journal-(?:index-thumb|cover-slot) ([a-z]+)$/)?.[1])
.filter((tag): tag is string => Boolean(tag));
expect(inSlot.length).toBeGreaterThan(0);
for (const tag of inSlot) expect(drawn, `.journal-… ${tag}`).toContain(tag);
});

it("switch off no ::before that no rule switches on", () => {
const last = (selector: string) => selector.split(" ").at(-1);
const content = (body: string) => declaration(body, "content");
for (const rule of all) {
for (const selector of rule.selectors) {
if (!selector.startsWith(".journal-") || !selector.endsWith("::before") || content(rule.body) !== "none") continue;
const on = all.some(
(other) =>
other.selectors.some((s) => last(s) === last(selector)) &&
content(other.body) !== undefined &&
content(other.body) !== "none",
);
expect(on, selector).toBe(true);
}
}
});
});
Loading