-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathreducedMotion.ts
More file actions
40 lines (34 loc) · 1.83 KB
/
Copy pathreducedMotion.ts
File metadata and controls
40 lines (34 loc) · 1.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import type { Path, SemanticIssue, AnyCondition } from '../types';
const MOTION_PREFERENCE_FEATURE = 'prefers-reduced-motion';
const NO_PREFERENCE = new RegExp(`${MOTION_PREFERENCE_FEATURE}\\s*:\\s*no-preference`);
const SCRUB_TRIGGERS = ['viewProgress', 'pointerMove'];
// `(prefers-reduced-motion: no-preference)` gates on motion being allowed, which is what the
// runtime already does for a scrub — redundant, not dead. Every other spelling of the feature
// (`: reduce`, or the boolean `(prefers-reduced-motion)`) selects the reduce side.
// `not (prefers-reduced-motion: no-preference)` reads as no-preference here — a deliberate
// false negative, cheaper than the false positives a looser test would produce.
function gatesOnReduce(predicate: string): boolean {
return predicate.includes(MOTION_PREFERENCE_FEATURE) && !NO_PREFERENCE.test(predicate);
}
export function checkReduceGatedScrub(
path: Path,
conditions: string[] | undefined,
configConditions: Record<string, AnyCondition>,
trigger?: string,
): SemanticIssue[] {
if (!trigger || !SCRUB_TRIGGERS.includes(trigger) || !conditions) return [];
const index = conditions.findIndex((conditionId) => {
const condition = configConditions[conditionId];
return condition?.type === 'media' && gatesOnReduce(condition.predicate);
});
if (index === -1) return [];
return [
{
code: 'custom',
params: { domainCode: 'REDUCE_GATED_SCRUB' },
path: [...path, 'conditions', index],
message: `Condition "${conditions[index]}" gates a \`${trigger}\` scrub on reduced motion, so it can never run — scrubbed effects are cancelled under \`(prefers-reduced-motion: reduce)\` whatever their conditions say. Give the reduced-motion alternative a time-based trigger such as \`viewEnter\`, or express it as a plain CSS rule.`,
severity: 'warning',
},
];
}