Scope touch-target floor to class-less controls; add .sf-touch-target - #648
Conversation
…rols The @media (pointer: coarse) minimum-touch-target floor in core/accessibility.css targeted every bare <button> and native control (only .sf-btn was carved out). It set both min-block-size and min-inline-size to var(--sf-touch-target) at a specificity higher than a single class, so it reached into markup the framework does not own — most visibly third-party page-builder and plugin controls. A narrow custom control such as a hamburger toggle (<button class="...-menu-toggle">) was stretched to 44px on both axes and could not be resized without !important. Scope the floor to class-less controls only (button:not([class]), the native inputs, select, summary, checkbox, radio). Any control carrying a class is treated as owned — by SLASHED (.sf-btn), the author, or a third-party widget — and keeps its owner's sizing. Genuinely bare, un-classed controls keep the WCAG 2.5.5 44px floor on both axes. This generalises the earlier .sf-btn carve-out into one rule: the framework never re-sizes a control someone else has styled. Matches the existing `a:not([class])` idiom in core/base.css and how Bootstrap/Tailwind keep touch-target enforcement on component classes rather than bare elements. - core/accessibility.css: rewrite selector + rationale comment - tests/button-touch.spec.js: add regression guard that a classed button (hamburger toggle) is exempt; refresh header/test names - docs/migration.md: breaking-change entry under Unreleased - demo/index.html: correct the touch-target description Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GgL6mqSxe2ihjaUCxx78eR
Scoping the automatic coarse-pointer floor to class-less controls (prev commit) left classed controls — i.e. almost all real-world controls — with no first-class way to request the 44px target beyond hand-rolling min-sizes or the .sf-btn-only --sf-btn-min-height knob. Add the explicit opt-in counterpart so the floor stays a safety net for bare markup while the everyday mechanism is a class. .sf-touch-target enforces min-block-size/min-inline-size: var(--sf-touch-target) on both axes and centres a short label via inline-flex (so it also works on inline elements like <a>). It lives in core/accessibility.css alongside .sr-only / .skip-link / .sf-focus-shadow, so it ships in every bundle, not just full. It is a single class (specificity 0,1,0) with no !important, so a component rule can still opt a specific control back out. Unlike the automatic floor it is not gated to a coarse pointer — an explicit opt-in is predictable on every device. - core/accessibility.css: add the .sf-touch-target helper + rationale - tests/a11y-patterns.spec.js: enforce-on-classed-control and overridable-without-!important guards - docs/migration.md: document .sf-touch-target as the primary remedy Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GgL6mqSxe2ihjaUCxx78eR
|
Warning Review limit reached
Next review available in: 26 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (8)
📝 WalkthroughWalkthroughThe coarse-pointer touch-target fallback now applies only to classless native controls. A new ChangesTouch target accessibility
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR narrows the automatic touch-target floor and adds an explicit opt-in utility. The main changes are:
Confidence Score: 4/5The touch-target selector and utility need fixes before merging.
core/accessibility.css and touch-target tests Important Files Changed
Reviews (1): Last reviewed commit: "feat(a11y): add .sf-touch-target opt-in ..." | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@core/accessibility.css`:
- Around line 130-151: Update the `--sf-touch-target` documentation in
llm-guide.md to explain that the automatic minimum-size floor skips controls
carrying a class and that `.sf-touch-target` explicitly opts controls into the
44px hit-area sizing. Preserve the existing token guidance and describe that the
opt-in applies regardless of pointer type.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: c913ea41-1d58-4e6c-8c7b-4338a939ac26
⛔ Files ignored due to path filters (1)
configurator/src/data/classes.generated.jsonis excluded by!**/*.generated.*
📒 Files selected for processing (9)
core/accessibility.cssdemo/index.htmldocs/api-index.jsondocs/api-index.mddocs/classes.mddocs/migration.mddocs/registry.jsontests/a11y-patterns.spec.jstests/button-touch.spec.js
…gallery Review feedback on #648: - Empty class="" bypassed the floor (P1): renderers routinely emit class="" for an unstyled control, and :not([class]) excluded it, so a genuinely-unstyled control could sit below 44px on touch. Scope the floor to "no effective class" via :where(button, …):not([class]:not([class=""])) — matches no class attribute OR an empty one. :where() keeps the element list at zero specificity. - .sf-touch-target replaced native display (P1): display: inline-flex stripped a classed <summary>'s list-item marker (and any control's native baseline). Drop display entirely — the class now sets only the two min-size properties, mirroring the automatic floor. Document that a purely inline target (<a>) needs its own display. - Class gallery coverage test failed: add the .sf-touch-target preview card and the #cov-data entry so the demo gallery matches the API class set. - llm-guide.md: document the class-less-only automatic floor and the .sf-touch-target opt-in (per CLAUDE.md llm-guide-sync mandate). - Tests: guard the empty-class floor (button-touch) and that .sf-touch-target leaves a <summary> as list-item (a11y-patterns). - migration.md: drop the centring claim; note sizing-only / inline caveat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GgL6mqSxe2ihjaUCxx78eR
`npm audit fix` bumps fast-uri 3.1.2 → 3.1.4, resolving the two high-severity advisories (GHSA-4c8g-83qw-93j6 host confusion via failed IDN canonicalization; GHSA-v2hh-gcrm-f6hx literal backslash authority delimiter). fast-uri is a deep devDependency transitive (stylelint → table → ajv → fast-uri); the bump is in-major and touches only package-lock.json. `npm audit` now reports 0 vulnerabilities; stylelint (lint:css) and the unit suite (129/129) still pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GgL6mqSxe2ihjaUCxx78eR
Summary
Refactors the WCAG 2.5.5 44px touch-target minimum in
core/accessibility.cssto scope it only to genuinely bare, un-classed controls (button:not([class]),input[type="…"]:not([class]), etc.). Any control that carries a class is now treated as owned — by the framework, the author, or a third-party widget — and keeps whatever size its owner provides.This fixes a long-standing issue where the blanket floor reached into third-party markup (e.g. page-builder hamburger toggles, plugin controls) and stretched narrow custom controls to 44px on both axes at a specificity higher than a single class, making them impossible to resize without
!important.Introduces
.sf-touch-target— a new explicit opt-in class for controls you own that need the 44px hit area. Unlike the automatic floor, it is not gated topointer: coarse, so it enforces the minimum everywhere, and it uses flexbox centering to keep short labels centred inside the enlarged box.Type
Checklist
feat:,fix:,docs:, …) — enforced by commitlintnpm run lint:csspasses (stylelint)npm run buildrebuildsdist/(bundles are git-ignored; CI rebuilds and stamps headers)npm testpasses (unit + Playwright e2e)npm run check:version)core/*.css,optional/*.css, ortoken-registry.jsonchanged (npm run check:llm-guide)npm run check:macros,check:registry,audit:check)CHANGELOG.mdupdated under## [Unreleased](for user-facing changes)Notes
Breaking change: Controls that carry any class no longer receive the automatic 44px floor on touch. This is intentional — the floor now only applies to bare, un-classed markup (the author's own quick
<button>text</button>). Controls with classes are considered owned and keep their owner's sizing.Migration path:
<button>/ native controls: unaffected, still get the 44px floor on touch.sf-touch-target(or setmin-block-size/min-inline-sizeyourself)!importantneeded)Tests added:
button-touch.spec.js: New regression guard verifying that a classed button (e.g.class="bricks-mobile-menu-toggle") is exempt from the floor on both axesa11y-patterns.spec.js: Two new tests for.sf-touch-target— one verifying it enforces ≥44px on both axes on a classed control, one verifying it is overridable by a plain class rule without!importantDocumentation:
docs/migration.md: Comprehensive breaking-change section explaining the rationale, the new.sf-touch-targetclass, and what changed for userscore/accessibility.css: Expanded comments explaining the scoping logic and the new opt-in classtests/button-touch.spec.js: Updated test comments to reflect the new:not([class])selectorGenerated artifacts:
docs/api-index.json,docs/api-index.md,docs/classes.md,docs/registry.json,configurator/src/data/classes.generated.json— all regenerated to include the new.sf-touch-targetclass (1 new PUBLIC class inhttps://claude.ai/code/session_01GgL6mqSxe2ihjaUCxx78eR
Summary by CodeRabbit
New Features
.sf-touch-targetutility to provide WCAG-compliant 44px minimum touch areas for interactive controls.Bug Fixes
Documentation