A heading names both sides only when something joins them

`DESIGN_BOTH_SIDES_HEADING` asked for a `do` within twelve characters of a
`don't`, which "Don't do this" and "What we don't do" both satisfy while
meaning only the one thing. A section headed either of those stopped
condemning the classes it names, so its counter-examples became declared
components and the rules stood down on exactly the misuse the heading
introduces.

The two sides have to be joined by something that pairs them now — and,
or, a slash, a comma — which is what "Dos and Don'ts" and "Do / Do not"
have and a sentence that happens to contain both words does not.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQBUunp8QttxZqihybNmtL
This commit is contained in:
Paul Bakaus
2026-09-20 19:39:48 -07:00
co-authored by Claude Fable 5.1
parent 0fea696a7f
commit 6233ba2e89
+17 -5
View File
@@ -78,11 +78,16 @@ re!(
);
// A heading that names both sides — "Do and Don't", "Dos and Don'ts",
// "Do / Do not" — introduces a section of both, so the subsections under it
// say which is which and the heading itself condemns nothing.
re!(
DESIGN_BOTH_SIDES_HEADING,
r"(?i)\bdos?\b[^\n]{0,12}?\b(?:do ?n[o']?ts?|do not)\b|\b(?:do ?n[o']?ts?|do not)\b[^\n]{0,12}?\bdos?\b".to_string()
);
// say which is which and the heading itself condemns nothing. The two sides
// have to be *joined* by something that pairs them: "Don't do this" and "What
// we don't do" also put a `do` beside a `don't`, and they mean only the one
// thing.
re!(DESIGN_BOTH_SIDES_HEADING, {
let joiner = format!(r"{WS}*(?:and|or|&|/|\||\+|,|vs\.?|versus){WS}*", WS = WS);
let affirmative = r"\bdo'?s?\b";
let negative = r"\b(?:do ?n[o']?ts?|do not)\b";
format!("(?i)(?:{affirmative}{joiner}{negative}|{negative}{joiner}{affirmative})")
});
re!(
FONT_SIZE_LITERAL_RE,
format!("^-?[{D}.]+(?:px|rem)$", D = "0-9")
@@ -2161,6 +2166,13 @@ mod tests {
### Don't\n\n- Reach for `.eyebrow`.\n";
assert_eq!(declared_component_selectors(md), vec![".kicker"]);
// A heading that only puts a `do` beside a `don't` is not a section
// of both, and still condemns what it names.
let md = "## Don't do this\n\n- `.eyebrow` above a heading.\n\n\
## What we don't do\n\n- `.tagline`\n\n\
## Components\n\n- `.kicker`\n";
assert_eq!(declared_component_selectors(md), vec![".kicker"]);
// What the negative words govern is their own clause. "No ALL CAPS
// outside the `.eyebrow` class" declares `.eyebrow`, and "four kinds
// and no more" declares all four.