From c97ed90df073f2a289236851a43b5e25c770052d Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Sun, 20 Sep 2026 19:51:21 -0700 Subject: [PATCH] A compound separator pairs the two sides too "Do's, and Don'ts" and "Do and/or Don't" put a comma and a conjunction, or a conjunction and a slash, between the two halves, and a joiner that accepted exactly one token read those headings as negative and dropped the classes their "Do" sections sanction. One joiner or several, up to four. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01LQBUunp8QttxZqihybNmtL --- crates/detect/src/design_system.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/detect/src/design_system.rs b/crates/detect/src/design_system.rs index ad2492b64..08c0a9a22 100644 --- a/crates/detect/src/design_system.rs +++ b/crates/detect/src/design_system.rs @@ -83,7 +83,10 @@ re!( // 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); + // One joiner or several: "Do's, and Don'ts" and "Do and/or Don't" pair + // the two sides with a comma plus a conjunction and with a conjunction + // plus a slash. + let joiner = format!(r"(?:{WS}*(?:and|or|&|/|\||\+|,|vs\.?|versus)){{1,4}}{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})") @@ -2166,6 +2169,12 @@ mod tests { ### Don't\n\n- Reach for `.eyebrow`.\n"; assert_eq!(declared_component_selectors(md), vec![".kicker"]); + // Compound separators pair the two sides just as well. + for heading in ["Do's, and Don'ts", "Do and/or Don't", "Don'ts / Dos"] { + let md = format!("## {heading}\n\n### Do\n\n- Use `.kicker`.\n"); + assert_eq!(declared_component_selectors(&md), vec![".kicker"], "{heading}"); + } + // 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\