From 98b01301d0ed4762310a050423bc9fc44a931363 Mon Sep 17 00:00:00 2001 From: Anilcan Cakir Date: Sat, 22 Aug 2026 01:09:25 +0300 Subject: [PATCH] docs(changelog): correct the _hasExplicitCrossWidth claim in the #180 entry The entry said the trailing newline caused a "miss" at that site whose cost was a redundant wrapper. There was no miss. `_hasExplicitCrossWidth` matches with `startsWith('w-')` rather than equality, and `'w-24\n'.startsWith('w-')` is true, so a newline never hid an explicit width from it. That also corrects the reasoning, not just the wording. The test written for that site passed before the fix because the predicate already worked, not because the consequence was unobservable, which is what the entry implied and what I had concluded. CodeRabbit raised this on #180 and I read its finding as claiming the entry asserted a behavioural fix, which it did not, so the thread closed with the mechanism agreed and this sentence left standing. Correcting it before it ships in the 1.4.1 release notes. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12e358d..02696ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ This project follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0. ### Fixed -- **A multi-line `className` hid its flex tokens from the row and column composers, and this project's own style guide is what put them there.** `.claude/rules/widgets.md` and `SKILL.md` both instruct a className covering 3+ concerns to be a triple-quoted string with one concern per line, so `flex-1` at the end of a line arrives at the composition helpers as `flex-1\n`. Three of them split on a single space and therefore never matched it. Two consequences, both reproduced: a `flex-1` child of an `overflow-hidden` row got wrapped a second time and threw `Incorrect use of ParentDataWidget` (`_selfWrapsInFlex`), and a `w-24 shrink-0` child in a crowded row shrank to its 50pt flex share instead of holding 96pt (`_hasShrinkZero`). The third site, `_hasExplicitCrossWidth`, is corrected for consistency with its own documented "in ANY state or breakpoint variant" intent, but no observable failure could be produced for it: a stretched column child still renders at the width it asked for, so the miss costs a redundant wrapper rather than a wrong layout. All five token scans in `WDiv` now share one hoisted `_whitespaceRegex`, which also stops the two that already split on whitespace from allocating a fresh `RegExp` on every pass through the composition path. (`lib/src/widgets/w_div.dart`, `test/widgets/w_div/multiline_classname_test.dart`) +- **A multi-line `className` hid its flex tokens from the row and column composers, and this project's own style guide is what put them there.** `.claude/rules/widgets.md` and `SKILL.md` both instruct a className covering 3+ concerns to be a triple-quoted string with one concern per line, so `flex-1` at the end of a line arrives at the composition helpers as `flex-1\n`. Three of them split on a single space and therefore never matched it. Two consequences, both reproduced: a `flex-1` child of an `overflow-hidden` row got wrapped a second time and threw `Incorrect use of ParentDataWidget` (`_selfWrapsInFlex`), and a `w-24 shrink-0` child in a crowded row shrank to its 50pt flex share instead of holding 96pt (`_hasShrinkZero`). The third site, `_hasExplicitCrossWidth`, never missed at all and is changed only so all five scans tokenize the same way: it matches with `startsWith('w-')` rather than equality, and `'w-24\n'.startsWith('w-')` is true, so a trailing newline never hid an explicit width from it. That is also the real reason a test written for it passed before the fix, and the reason it ships without one. All five token scans in `WDiv` now share one hoisted `_whitespaceRegex`, which also stops the two that already split on whitespace from allocating a fresh `RegExp` on every pass through the composition path. (`lib/src/widgets/w_div.dart`, `test/widgets/w_div/multiline_classname_test.dart`) - **Every hoverable `WDiv` announced itself as a button that cannot be pressed.** `WDiv` auto-wraps into a `WAnchor` whenever its className carries `hover:`, `focus:` or `active:`, purely to get the state this widget tracks, and `WAnchor` published `Semantics(button: true)` unconditionally: with no gesture behind it. So a decorative card styled `hover:bg-slate-100` reached assistive technology as a control. Measured on `WDiv(className: 'px-4 py-3 hover:bg-slate-100', child: WText('Latency'))`: one button node labelled `Latency` whose action set was `focus` alone, with no `tap`. A screen reader offers it as a button, the user activates it, and nothing happens, because there is no gesture in the tree to run. A gestureless, UNLABELLED `WAnchor` now publishes no semantics node of its own and lets its descendants speak, which is what a state propagator should do (an explicit `semanticLabel` still publishes the named node either way, which is how a disabled control reports that it exists and is unavailable; `WDiv`'s auto-wrap never passes one): the same card keeps its `Latency` label and loses the role. An anchor with `onTap`, `onLongPress` or `onDoubleTap` is unchanged, so `WButton` and every real control keep their single named button node with its tap action, and an explicit `semanticLabel` still wins for the icon-only case that has no child text to merge. One claim to retire with this: the nesting `WAnchor(onTap:) > WDiv(hover:...)` did NOT announce twice, though it published two button nodes in the widget tree. The inner one carried `isMergedIntoParent`, so Flutter folded it into the real tap surface and never sent it to the platform. A reading that counts raw tree nodes rather than platform nodes will report a duplicate announcement that no screen reader ever made. `MergeSemantics` goes with the role rather than staying, and that was measured too: keeping it made a gestureless wrapper ABSORB a descendant control's role and actions, so a locked region tile in a consumer app swallowed the display-only `WCheckbox` inside it and published itself as "US West, button" carrying the checkbox's tap. That is the same bogus claim in a new place, so a styling-only wrapper now publishes nothing at all. (`lib/src/widgets/w_anchor.dart`, `doc/widgets/w-anchor.md`, `doc/widgets/w-div.md`, `skills/wind-ui/SKILL.md`, `skills/wind-ui/references/widgets.md`)