diff --git a/web/src/components/sections/BuildPanel.module.css b/web/src/components/sections/BuildPanel.module.css index a6992ac5b..085c81a8e 100644 --- a/web/src/components/sections/BuildPanel.module.css +++ b/web/src/components/sections/BuildPanel.module.css @@ -170,7 +170,10 @@ text-transform: uppercase; } -.otherPaths { +/* Through .buildPanel, the same element as .panel-body: the global + `.panel-body p` (0,1,1) beat the bare class and set this line at 20px with + a 28px bottom margin. */ +.buildPanel .otherPaths { margin: 16px 0 0; color: var(--pf-ink-muted); font-size: var(--pf-fs-body); diff --git a/web/src/components/sections/PatternPanel.module.css b/web/src/components/sections/PatternPanel.module.css index f47c0b9ef..c99363c1e 100644 --- a/web/src/components/sections/PatternPanel.module.css +++ b/web/src/components/sections/PatternPanel.module.css @@ -280,7 +280,11 @@ line-height: 1.5; } -.advancedNote { +/* .panel-body p is a global rule at (0,1,1), 20px (17px on a phone) with a + 28px bottom margin, and it outranks a bare module class. Both notes are + scoped through .body to win it. At 1440 they were 20px under an 18px title, + and the To hardware block ran 572px. */ +.body .advancedNote { margin: 14px 0 0; color: var(--pf-ink-muted); font-size: var(--pf-fs-row-d); @@ -341,7 +345,7 @@ border-color: var(--pf-cream-2); } -.hardwareNote { +.body .hardwareNote { margin: 0; color: var(--pf-cream); opacity: 0.75; diff --git a/web/src/components/sections/PatternPanel.tsx b/web/src/components/sections/PatternPanel.tsx index e62baf492..402b542de 100644 --- a/web/src/components/sections/PatternPanel.tsx +++ b/web/src/components/sections/PatternPanel.tsx @@ -156,7 +156,7 @@ export default function PatternPanel({ content, isActive }: PatternPanelProps) {
{content.subtitle}
-m[1]))]; + + for (const name of names) { + it(`${panel} .${name} is scoped through a second class`, () => { + expect(rules, `a bare .${name} loses to .panel-body p`).not.toContain(`.${name}`); + const scoped = rules.find((s) => new RegExp(`^\\.(\\w+) \\.${name}$`).test(s)); + expect(scoped, `no ".scope .${name}" rule`).toBeDefined(); + const scope = scoped!.split(" ")[0].slice(1); + expect(tsx, `.${scope} is not on an element in ${panel}.tsx`).toContain(`styles.${scope}`); + }); + } + } + + it("finds the four notes this guards", () => { + const all = panels.flatMap((panel) => + [...read(`${panel}.tsx`).matchAll(/
m[1]), + ); + expect(all).toEqual(expect.arrayContaining(["advancedNote", "hardwareNote", "otherPaths"])); + expect(all.filter((n) => n === "hardwareNote")).toHaveLength(2); + }); +});