Allowing parametric rules
Jym77 opened this issue · 3 comments
Some though I've had recently about a possible augmentation to the rule format, notably while working on the "Target Size" rules.
We have a few cases of rules that are very similar but checking for a different threshold because there are two "levels" of the same SC (e.g., color contrast). Sometimes one of the rule may have extra conditions. We currently write two separate rules, but that is very WET, and thus creates maintenance overload when we need to edit them (e.g., act-rules/act-rules.github.io#1961 or act-rules/act-rules.github.io#1978). Plus cognitive overload for readers to figure out that the rules are indeed the same apart from a threshold somewhere.
It would be more convenient for us to have a shared rule with one (or more) "parameter", and then the actual rules would use it, with specific parameters. This can fit within the composite rules framework.
For the Target Size, we're likely going to have two rules, one with "more than 44×44", another one with "more than 24×24; or enough spacing". I feel that if the second one was also 44×44, we'd just go for a composite rule. We can still go for a composite rule, but we'll need to duplicate the "target large enough" rule just to change a number. Given that it will likely already be a fairly complex rule, that doesn't feel good.
We're likely going to have:
(Rule A)
Applicability: clickable stuff
Expectation: 44×44px, inline, essential, or some equivalent instrument.(Rule B)
Applicability: clickable stuff <- same
Expectation: 24×24px, inline, essential, or some equivalent instrument; or spacing <- only differ in number and extra spacing condition, but that is not obvious to see.
We can maybe make things a bit more streamlined with
(Rule A)
Applicability: clickable stuff
Expectation: 44×44px, inline, essential, or some equivalent instrument.(Rule B1)
Applicability: clickable stuff <- same
Expectation: 24×24px, inline, essential, or some equivalent instrument. <- only differ in number(Rule B2)
Applicability: clickable stuff <- same
Expectation: spacing <- totally different(Rule B (composite))
Applicability: clickable stuff <- same
Expectation: neither Rule B1 nor B2 fails.
But I feel it would be better in term of DRY and especially DRY the complex bits to have:
(parametric rule)
Applicability: clickable stuff
Expectation: size is X×Xpx, inline, essential, or some equivalent instrument.(Rule A) ("composite" but just calling a parametric)
Applicability: clickable stuff
Expectation: (parametric rule with X=44) doesn't fail.(Rule B2)
Applicability: clickable stuff <- same
Expectation: spacing <- totally different(Rule B (composite))
Applicability: clickable stuff <- same
Expectation: neither (parametric rule with X=24) nor B2 fails.
While this does dilute a bit rule A, it makes the connection (and differences) between rule A and B much more obvious. And it lowers the maintenance cost on our side, since we only need to change the parametric rule, not both A and B1.
The main problem I see with that is that the parametric rule is a bit "abstract" (in the Object Oriented meaning) and needs to be called. Which notably makes it a bit more tricky to add test cases to it (especially as the parameters would need to be baked in for implementation report generation). But the test cases can be in the "calling" rules.
We'd also need to be a bit cautious on which parameters to allow to avoid too much leeway. Likely restrict them to numbers and booleans 🤔
This makes sense for the contrast rules, but is a bit more complex for the Target Size rules.
The 2.5.5 Target Size (Enhanced) SC ensures targets are easy to click. It triggers on anything smaller than 44x44 (apart from equivalent, inline, essential and UA defined)
The 2.5.8 Target Size (Minimum) SC is similar but with a 24x24 threshold and an extra (and very complicated) exception:
Spacing: Undersized targets (those less than 24 by 24 CSS pixels) are positioned so that if a 24 CSS pixel diameter circle is centered on the bounding box of each, the circles do not intersect another target or the circle for another undersized target
Or put another way:
2.5.5 is about making it easy to hit the right target
2.5.8 is about making it hard to hit the wrong target
Is there a way to move some of the applicability or expectation into something like a shared definition?
Yes, I think for Target size, we'd need a parametric rule for X×Xpx
, and another rule for the complex exception (only); and the 2.5.8 rule would be composite of both, while the 2.5.5 rule would be only using the parametric one.
(actually, even without parametric rules, I'm strongly thinking about making a composite rule for 2.5.8, just because both parts (size and distance) are complex enough alone and having both in a single atomic rule will be a nightmare to read and to maintain).
Yep, I think composition is the right way to do it. There's some stuff that's shared (parameterised applicability and/or expectation) and other stuff that's specific to the rule (accessibility mappings, test cases)
We do this by having common calculations for target width, height, etc. The individual rules then assemble those to produce the desired outcome. So it's something like this:
targetSizeEnhanced() {
if ( targetSize.Width < 44 || targetSize.Height < 44 ) {
if ( !targetSizeCommonExceptions() )
Fail();
}
targetSizeMinimum() {
if ( targetSize.Width < 24 || targetSize.Height < 24 ) {
if ( !targetSizeCommonExceptions() && targetTooClose() )
Fail();
}
That's why I was suggesting something like a shared definition - you can hide the complexity in the definitions of targetSize, targetTooClose and "call" those definitions from simple looking rules.