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
60 changes: 4 additions & 56 deletions web/src/app/pattern-lab/labCss.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
// On the same screen Del was 37×28px, the layer eye 20×17px and Push 40×22px,
// and the Pixel status bar was 51px, two lines with the mouse gestures.

import { readFileSync, readdirSync } from "node:fs";
import { readFileSync } from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { bareCss, readers } from "../../../test/cssModules";
import { NARROW_DOCK_QUERY } from "./panels/registry";

const css = (name: string) => readFileSync(path.resolve(__dirname, name), "utf8");
Expand Down Expand Up @@ -182,59 +183,6 @@ describe("Pixel status line on a touch screen", () => {
});
});

/** Every `.tsx` and `.ts` under `src/`, tests left out. */
const src = path.resolve(__dirname, "../..");
const sources = (readdirSync(src, { recursive: true }) as string[])
.filter((file) => /\.tsx?$/.test(file) && !/\.test\.tsx?$/.test(file))
.map((file) => path.join(src, file))
.map((file) => ({ file, text: readFileSync(file, "utf8") }));

/** A stylesheet with its comments and quoted strings taken out. */
const bareCss = (name: string) => css(name).replace(/\/\*[\s\S]*?\*\//g, "").replace(/"[^"]*"|'[^']*'/g, "");

/** Each class of the module at `file` that a file importing it reads as
* `styles.name` or `styles["name"]`, with the files that read it. Null when
* one of them reads the module by a computed name. */
function readers(file: string): Map<string, Set<string>> | null {
const read = new Map<string, Set<string>>();
for (const { file: from, text } of sources) {
for (const [, local, spec] of text.matchAll(/import (\w+) from "([^"]+\.module\.css)"/g)) {
const target = spec.startsWith("@/") ? path.join(src, spec.slice(2)) : path.resolve(path.dirname(from), spec);
if (target !== file) continue;
if (new RegExp(`\\b${local}\\[(?!["'\`])`).test(text)) return null;
for (const m of text.matchAll(new RegExp(`\\b${local}(?:\\.(\\w+)|\\[["'\`](\\w+)["'\`]\\])`, "g"))) {
const name = m[1] ?? m[2];
read.set(name, (read.get(name) ?? new Set<string>()).add(from));
}
}
}
return read;
}

describe("Lab CSS modules", () => {
// Until 24 October 2026 these files held 13 classes that no component put
// on an element, and 220 lines of rules for them. A class counts as used
// when a file importing its module reads `styles.name` or `styles["name"]`,
// or a rule in the same file composes it. A module read by a computed name
// counts as used whole.
const modules = (readdirSync(__dirname, { recursive: true }) as string[]).filter((file) => file.endsWith(".module.css"));

it("define no class that no component uses", () => {
const unused: Record<string, string[]> = {};
for (const name of modules) {
const bare = bareCss(name);
const defined = new Set([...bare.matchAll(/\.(-?[_a-zA-Z][\w-]*)/g)].map((m) => m[1]));
const used = new Set([...bare.matchAll(/composes:([^;]+);/g)].flatMap((m) => m[1].trim().split(/\s+/)));
const read = readers(path.join(__dirname, name));
for (const cls of read?.keys() ?? []) used.add(cls);
const dead = read === null ? [] : [...defined].filter((cls) => !used.has(cls)).sort();
if (dead.length) unused[name] = dead;
}
expect(modules.length).toBeGreaterThan(0);
expect(unused).toEqual({});
});
});

describe("PatternLab.module.css", () => {
// The file is for what two or more files draw with. Until 25 October 2026
// it also held 22 rules only the shell read, for the header, and the dark
Expand All @@ -246,7 +194,7 @@ describe("PatternLab.module.css", () => {
expect(read).not.toBeNull();
const by = (cls: string) => [...(read?.get(cls) ?? [])];
const files = new Map<string, Set<string>>();
for (const [, selectors] of bareCss("PatternLab.module.css").matchAll(/([^{}]+)\{[^{}]*\}/g)) {
for (const [, selectors] of bareCss(path.resolve(__dirname, "PatternLab.module.css")).matchAll(/([^{}]+)\{[^{}]*\}/g)) {
const plain = selectors.split(",").flatMap((part) => /^\s*\.([_a-zA-Z][\w-]*)\s*$/.exec(part)?.[1] ?? []);
for (const [, cls] of selectors.matchAll(/\.(-?[_a-zA-Z][\w-]*)/g)) {
const reach = plain.includes(cls) ? plain.flatMap(by) : by(cls);
Expand All @@ -269,7 +217,7 @@ describe("LabPanels.module.css", () => {
const read = readers(path.resolve(__dirname, "LabPanels.module.css"));
expect(read).not.toBeNull();
// Only names the file defines: registry.tsx also reads `def.dock.ref`.
const defined = new Set([...bareCss("LabPanels.module.css").matchAll(/\.(-?[_a-zA-Z][\w-]*)/g)].map((m) => m[1]));
const defined = new Set([...bareCss(path.resolve(__dirname, "LabPanels.module.css")).matchAll(/\.(-?[_a-zA-Z][\w-]*)/g)].map((m) => m[1]));
const panels = path.join(__dirname, "panels") + path.sep;
const alone = [...(read ?? [])]
.filter(([cls, from]) => defined.has(cls) && from.size === 1 && [...from].every((file) => file.startsWith(panels)))
Expand Down
105 changes: 0 additions & 105 deletions web/src/app/roadmap/Roadmap.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -103,54 +103,6 @@
}
}

.legend {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 20px;
}

.legendItem {
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--pf-ink-muted);
font-family: var(--pf-mono);
font-size: 12.5px;
letter-spacing: 0.05em;
text-transform: uppercase;
}

.legendDrag {
color: var(--pf-ink-faint);
font-family: var(--pf-mono);
font-size: 12.5px;
letter-spacing: 0.05em;
text-transform: uppercase;
}

.swatchDone,
.swatchPlanned {
width: 22px;
height: 14px;
border-radius: 0;
}

.swatchDone {
border: 1.5px solid var(--pf-ink);
background: #fff;
}

.swatchPlanned {
border: 1.5px dashed var(--pf-ink-faint);
}

.swatchDep {
width: 24px;
height: 0;
border-top: 2px dashed var(--pf-ink-faint);
}

/* .viewport does NOT clip, so the popup can never be cut off by the
pannable canvas underneath it — only the canvas itself clips. */
.viewport {
Expand Down Expand Up @@ -182,44 +134,6 @@
will-change: transform;
}

.zoomGroup {
display: inline-flex;
align-items: center;
border: 1px solid var(--pf-rule);
border-radius: 0;
overflow: hidden;
}

.zoomBtn,
.zoomVal {
appearance: none;
border: none;
background: transparent;
color: var(--pf-ink-muted);
font-family: var(--pf-mono);
cursor: pointer;
}

.zoomBtn {
padding: 7px 14px;
font-size: 16px;
line-height: 1;
}

.zoomVal {
padding: 9px 10px;
min-width: 56px;
font-size: 12.5px;
letter-spacing: 0.04em;
border-inline: 1px solid var(--pf-rule);
}

.zoomBtn:hover,
.zoomVal:hover {
color: var(--pf-ink);
background: var(--pf-cream-2);
}

.mapSvg {
display: block;
overflow: visible;
Expand Down Expand Up @@ -249,7 +163,6 @@
letter-spacing: -0.01em;
}

.gridLine,
.gridLineHorizontal {
stroke: var(--pf-rule-soft);
stroke-width: 1;
Expand Down Expand Up @@ -508,21 +421,3 @@
text-decoration: underline;
text-underline-offset: 3px;
}

.mapCaption {
position: absolute;
left: 16px;
bottom: 12px;
z-index: 5;
max-width: 620px;
margin: 0;
padding: 8px 12px;
border-radius: 0;
background: rgba(251, 248, 242, 0.88);
color: var(--pf-ink-faint);
font-family: var(--pf-mono);
font-size: 12px;
letter-spacing: 0.03em;
line-height: 1.6;
pointer-events: none;
}
9 changes: 0 additions & 9 deletions web/src/components/community/Atlas.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -361,15 +361,6 @@
margin: 6px 0 14px;
}

.panelWell {
position: relative;
width: 140px;
height: 280px;
border: 1px solid var(--pfc-hair);
background: var(--pfc-well);
overflow: hidden;
}

.openLink {
display: inline-block;
margin-top: 4px;
Expand Down
102 changes: 1 addition & 101 deletions web/src/components/community/Community.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2766,35 +2766,6 @@
font-weight: 500;
}

/* ── Sign-in gate ──
Never a wall: everything plays without an account, so the prompt appears
inline at the moment something needs keeping, with the knobs left alone. */
.gatePanel {
display: flex;
gap: 14px;
align-items: center;
border: 1px solid rgba(244, 239, 230, 0.16);
background: var(--pfc-panel);
padding: 16px 18px;
max-width: 560px;
}

.gateText {
display: flex;
flex-direction: column;
gap: 3px;
}

.gateTitle {
font-size: 14px;
font-weight: 500;
}

.gateSub {
font-size: 12.5px;
color: var(--pfc-ink-muted);
}

/* ── Firmware build modal ── */

.buildStatusLine {
Expand Down Expand Up @@ -2876,20 +2847,6 @@
color: var(--pfc-led);
}

.sectionLink {
font-family: var(--pf-mono, monospace);
font-size: 11px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--pfc-ink);
text-decoration: none;
margin-left: auto;
}

.sectionLink:hover {
color: var(--pfc-led);
}

.deckSection {
padding-bottom: 30px;
margin-bottom: 28px;
Expand Down Expand Up @@ -3031,22 +2988,7 @@
/* An anchor that has to sit in a row of buttons and match them. It is a real
link rather than a button because it leaves for the device's own origin —
middle-click and "open in new tab" should work, and a click handler would
take those away. */
.btnLink {
composes: btn;
display: inline-flex;
align-items: center;
text-decoration: none;
/* No line-height override: the buttons beside it inherit the body's, and
resetting it here made the link 6px shorter than everything in the row. */
}

.btnLink[aria-disabled="true"] {
opacity: 0.5;
pointer-events: none;
}

/* The accent weight of the same thing — "this leaves the page and touches
take those away. The accent weight says "this leaves the page and touches
your board", which is exactly what it does.
`composes: btn btnAccent` names both on purpose: composing btnAccent alone
emits only two classes here, dropping the btn underneath it, so the fill
Expand Down Expand Up @@ -3315,27 +3257,6 @@
border-color: var(--pfc-ink);
}

/* Fresh from the wall: a single dense row under the decks, so the newest work
is one glance away from the curated shelf. */
.freshStrip {
display: grid;
grid-template-columns: repeat(8, minmax(0, 1fr));
gap: 12px;
}

@media (max-width: 1100px) {
.freshStrip {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
}

@media (max-width: 768px), (hover: none) {
.freshStrip {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
}
}

/* ── Deck page ── */

.deckHead {
Expand Down Expand Up @@ -3754,16 +3675,6 @@
color: var(--pfc-ink);
}

.introRow {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
font-size: 13.5px;
color: var(--pfc-ink-muted);
flex: none;
}

.postComposer {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -3826,13 +3737,6 @@
gap: 10px;
}

.composerCount {
flex: 1;
font-family: var(--pf-mono, monospace);
font-size: 11px;
color: var(--pfc-ink-faint);
}

.postList {
list-style: none;
margin: 0;
Expand Down Expand Up @@ -5735,10 +5639,6 @@ button.legendWalk:hover {
gap: 8px;
}

.introRow {
font-size: 13px;
}

.feedControls {
gap: 12px;
}
Expand Down
1 change: 0 additions & 1 deletion web/src/components/guide/ConsoleWindow.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
--cw-ink: #efe9dd;
--cw-ink-2: rgba(239, 233, 221, 0.72);
--cw-ink-3: rgba(239, 233, 221, 0.46);
--cw-hair: rgba(239, 233, 221, 0.1);
--cw-hair-2: rgba(239, 233, 221, 0.18);
--cw-led: #ff6a3d;
/* The console's own ground (the pages' --cream, light by default), so a
Expand Down
Loading
Loading