Source code for rational.styling.responsive

"""Named viewport breakpoints and container sizes.

Names are conventions, not device detection: the browser evaluates the
generated media/container queries; Python never inspects the viewport.
"""

from __future__ import annotations

from collections.abc import Mapping
from dataclasses import dataclass


[docs] @dataclass(frozen=True, slots=True) class Breakpoint: min: int | None = None max: int | None = None def condition(self) -> str: parts = [] if self.min is not None: parts.append(f"(min-width: {self.min}px)") if self.max is not None: parts.append(f"(max-width: {self.max - 0.02:g}px)") if not parts: raise ValueError("a breakpoint needs min and/or max") return " and ".join(parts) def sort_key(self) -> tuple[int, int]: # Later rules win, so the most specific condition is emitted last: # max-only queries widest first, then bounded ranges, then min-only ascending. if self.min is None: return (0, -(self.max or 0)) if self.max is not None: return (1, self.min) return (2, self.min)
DEFAULT_BREAKPOINTS: Mapping[str, Breakpoint] = { "mobile": Breakpoint(max=640), "tablet": Breakpoint(min=640, max=1024), "narrow": Breakpoint(max=1024), "desktop": Breakpoint(min=1024), "sm": Breakpoint(min=640), "md": Breakpoint(min=768), "lg": Breakpoint(min=1024), "xl": Breakpoint(min=1280), } DEFAULT_CONTAINERS: Mapping[str, Breakpoint] = { "compact": Breakpoint(max=480), "regular": Breakpoint(min=480), "wide": Breakpoint(min=900), }