/** * Choosing a type size that fits the space available. * * The panel cannot scroll and cannot reflow gracefully: text that does not fit * is text that spills over a neighbour or falls off the bottom edge. The shop * can legitimately enter three opening periods in a day, none of them on a * round hour, and write a long message on top — a combination that overflowed * both the week strip and the bottom banner before this existed. * * The width estimate is deliberately crude. Measuring glyphs properly would * mean loading the font here and would still be an estimate, because the * renderer applies its own kerning. Erring small costs a point of type; erring * large costs a broken screen. */ /** Average glyph width as a fraction of the type size, for Inter's digits and lowercase. */ const WIDTH_RATIO = 0.54; export type FitOptions = { /** The width the text has to live in, in pixels. */ available: number; /** The size to use when the text is short enough. */ preferred: number; /** Never go below this: smaller stops being readable from the pavement. */ minimum: number; }; export function fitFontSize(text: string, { available, preferred, minimum }: FitOptions): number { if (text.length === 0) { return preferred; } const fitting = available / (text.length * WIDTH_RATIO); return Math.max(minimum, Math.min(preferred, Math.floor(fitting))); }