// @ts-check /** * Warn when a CSS `gridTemplateColumns` value uses a bare `Nfr` track * without `minmax(0, ...)`. Default `min-width: auto` makes grid items * refuse to shrink below their content, causing overflow. * * Pattern F in docs/ui/UI_AUDIT.md. */ const HAS_FR_RE = /\b\d*\.?\d*fr\b/; const HAS_MINMAX_RE = /minmax\s*\(/; function checkValue(context, node, raw) { if (typeof raw !== 'string' || !HAS_FR_RE.test(raw)) return; if (!HAS_MINMAX_RE.test(raw)) { context.report({ node, messageId: 'bare', data: { value: raw } }); return; } // Has minmax somewhere — check whether *every* fr is wrapped. const stripped = raw.replace(/minmax\s*\([^)]*\)/g, 'MM'); if (HAS_FR_RE.test(stripped)) { context.report({ node, messageId: 'bare', data: { value: raw } }); } } export default { meta: { type: 'suggestion', docs: { description: 'grid 1fr tracks should be wrapped in minmax(0, ...) to allow shrinking' }, schema: [], messages: { bare: 'gridTemplateColumns "{{ value }}" has a bare Nfr track. Wrap each in minmax(0, Nfr) so cell content can shrink below min-content.', }, }, create(context) { function visitProperty(node) { if (!node.key) return; const keyName = node.key.name || (node.key.type === 'Literal' && node.key.value); if (keyName !== 'gridTemplateColumns') return; const v = node.value; if (!v) return; if (v.type === 'Literal' && typeof v.value === 'string') { checkValue(context, v, v.value); } else if (v.type === 'TemplateLiteral') { const raw = v.quasis.map(q => q.value.cooked).join('${...}'); checkValue(context, v, raw); } } return { Property: visitProperty }; }, };