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
193 changes: 6 additions & 187 deletions web/src/app/pattern-lab/PatternLab.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -88,57 +88,16 @@
color: #171512;
}

.header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
padding-bottom: 18px;
border-bottom: 1px solid rgba(23, 21, 18, 0.18);
}

.headerMeta,
.stats,
.actionRow {
.stats {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 10px;
}

.headerMeta,
.stats {
gap: 8px;
color: rgba(23, 21, 18, 0.62);
font-family: var(--font-jetbrains), monospace;
font-size: 12px;
line-height: 1.45;
text-transform: uppercase;
}

.previewHeader {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-bottom: 16px;
}

.editorHeader {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 16px;
}

.stats {
display: flex;
text-transform: none;
align-items: center;
gap: 8px;
color: rgba(23, 21, 18, 0.62);
font-family: var(--font-jetbrains), monospace;
font-size: 12px;
}

.dotSep {
Expand Down Expand Up @@ -248,116 +207,12 @@
overflow-wrap: anywhere;
}

.actionRow {
margin-top: 12px;
}

.actionRow button,
.editorHeader button {
min-height: 38px;
border: 1px solid rgba(23, 21, 18, 0.22);
background: #ffffff;
color: #171512;
padding: 0 12px;
font: inherit;
font-family: var(--font-jetbrains), monospace;
font-size: 12px;
text-transform: uppercase;
cursor: pointer;
}

.actionRow button:hover,
.editorHeader button:hover {
border-color: #171512;
}

.darkButton,
.editorHeader button {
.darkButton {
background: #171512 !important;
color: #f7f3ea !important;
border-color: #171512 !important;
}

.editorHeader .guideButton {
background: #e8dcc2 !important;
color: #171512 !important;
border-color: #171512 !important;
}

.editorHeader .guideButton:hover {
background: #d9c8a4 !important;
}

.editorHeader .pasteButton {
display: none;
background: #e8552e !important;
color: #ffffff !important;
border-color: #e8552e !important;
font-weight: 600;
}

.editorHeader .pasteButton:hover {
background: #d44722 !important;
border-color: #d44722 !important;
}

.editorHeader .clearButton {
display: none;
background: #ffffff !important;
color: #171512 !important;
border: 1px solid rgba(23, 21, 18, 0.3) !important;
}

.editorHeader .clearButton:hover {
background: #f0eae1 !important;
border-color: #171512 !important;
}

.editorHeader {
flex: 0 0 auto;
min-height: 64px;
margin: 0;
padding: 12px 14px;
border-bottom: 1px solid rgba(23, 21, 18, 0.18);
}

.editorHeader span {
display: block;
font-size: 14px;
font-weight: 700;
}

.editorHeader button:disabled {
opacity: 0.5;
cursor: default;
}

.viewToggle {
display: flex;
gap: 0;
}

.editorHeader .viewToggle button {
min-height: 32px;
border: 1px solid #171512 !important;
background: #ffffff !important;
color: #171512 !important;
padding: 0 14px;
font-family: var(--font-jetbrains), monospace;
font-size: 12px;
text-transform: uppercase;
cursor: pointer;
}

.editorHeader .viewToggle button + button {
border-left: none !important;
}

.editorHeader .viewToggle button[data-active="true"] {
background: #171512 !important;
color: #f7f3ea !important;
}

.variantActions {
display: flex;
gap: 8px;
Expand Down Expand Up @@ -503,46 +358,10 @@
}

@media (max-width: 980px) {
.header,
.previewHeader,
.editorHeader {
align-items: flex-start;
flex-direction: column;
}

.previewHeader {
gap: 10px;
margin-bottom: 12px;
}

.editorHeader button {
min-height: 36px;
padding: 0 10px;
font-size: 11px;
}

.editorHeader .pasteButton,
.editorHeader .clearButton {
display: inline-flex;
}

.stats {
justify-content: flex-start;
text-align: left;
}

/* Action buttons become full-width rows — easy to hit, nothing truncated. */
.actionRow {
display: grid;
grid-template-columns: 1fr;
gap: 8px;
}

.actionRow button {
width: 100%;
min-height: 44px;
}

justify-content: flex-start;
text-align: left;
}
}

/* ── Community fork badge ── */
Expand Down
39 changes: 38 additions & 1 deletion web/src/app/pattern-lab/labCss.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// 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 } from "node:fs";
import { readFileSync, readdirSync } from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
import { describe, expect, it } from "vitest";
Expand Down Expand Up @@ -180,3 +180,40 @@ describe("Pixel status line on a touch screen", () => {
expect(mediaRule(dock, "(hover: none)", ".pixelStatus")).toBe("");
});
});

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 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") }));
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 file = path.join(__dirname, name);
const bare = css(name).replace(/\/\*[\s\S]*?\*\//g, "").replace(/"[^"]*"|'[^']*'/g, "");
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+/)));
let computed = false;
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;
for (const m of text.matchAll(new RegExp(`\\b${local}(?:\\.(\\w+)|\\[["'\`](\\w+)["'\`]\\])`, "g"))) used.add(m[1] ?? m[2]);
if (new RegExp(`\\b${local}\\[(?!["'\`])`).test(text)) computed = true;
}
}
const dead = computed ? [] : [...defined].filter((cls) => !used.has(cls)).sort();
if (dead.length) unused[name] = dead;
}
expect(modules.length).toBeGreaterThan(0);
expect(unused).toEqual({});
});
});
33 changes: 0 additions & 33 deletions web/src/app/pattern-lab/panels/CapturePanel.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -107,39 +107,6 @@
overflow: auto;
}

.stageBox {
flex: 1 1 auto;
min-height: 120px;
min-width: 0;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
background: #1a1816;
border: 1px solid #171512;
}

/* The canvas is sized by its own bitmap and only ever scaled DOWN to fit —
a replaced element keeps its ratio under max-width/max-height. */
.stageCanvas {
display: block;
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08);
}

/* Transparency shows as the usual checkerboard, under the picture only. */
.stageCanvasTransparent {
background-color: #d9d4ca;
background-image:
linear-gradient(45deg, #bdb7aa 25%, transparent 25%, transparent 75%, #bdb7aa 75%),
linear-gradient(45deg, #bdb7aa 25%, transparent 25%, transparent 75%, #bdb7aa 75%);
background-size: 16px 16px;
background-position: 0 0, 8px 8px;
}

/* The auto look's verdict, full width under the output controls. */
.verdict {
flex: 1 1 100%;
Expand Down
6 changes: 0 additions & 6 deletions web/src/app/pattern-lab/panels/DirectorPanel.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,3 @@
color: #dc2626;
font-weight: 600;
}

.hint {
padding: 10px 12px;
font-size: 12px;
color: #777;
}
Loading