'desktop', 'max' => null]]; /** The widest breakpoint a media query will be emitted for. */ private const MAX_WIDTH = 20000; /** * Validate a breakpoint list. * * The first entry is the base and carries no width. Every later entry needs a width, * because that is what its media query is built from; one without is dropped rather * than defaulted, so a malformed list cannot widen a rule's reach. * * @param mixed $raw * @return array */ public static function breakpoints($raw): array { if (!is_array($raw) || !Value::isList($raw) || $raw === []) { return self::DEFAULT_BREAKPOINTS; } $out = []; $seen = []; foreach ($raw as $bp) { $id = Value::get($bp, 'id'); if (!is_string($id) || !preg_match('/^[a-z][a-z0-9_-]{0,31}$/', $id) || isset($seen[$id])) { continue; } $max = Value::get($bp, 'max'); if ($out === []) { /* The base breakpoint: no media query, so no width. */ $seen[$id] = true; $out[] = ['id' => $id, 'max' => null]; continue; } if (!is_int($max) && !is_float($max) && !(is_string($max) && preg_match('/^\d{1,5}$/', $max))) { continue; } $max = (int) $max; if ($max < 1 || $max > self::MAX_WIDTH) { continue; } $seen[$id] = true; $out[] = ['id' => $id, 'max' => $max]; } return $out === [] ? self::DEFAULT_BREAKPOINTS : $out; } }