core/stylelint/no-large-viewport-unit.stylelint.rule.ts
core/stylelint/no-large-viewport-unit.stylelint.rule.ts is a file in GovLab Quality. 40 lines of code and 7 definitions.
import type { Declaration, Root } from "postcss";
import stylelint, { type PostcssResult, type Rule } from "stylelint";
import type { GovlabStylelintMeta } from "#types/stylelint.types";
import { valueUnits } from "#core/parsers/css.parser";
import { withRuleId } from "#core/formatters/stylelint.formatter";
const { createPlugin, utils } = stylelint;
const ruleName = "govlab/no-large-viewport-unit";
const ruleId = "no_large_viewport_unit";
export const RULE_META: GovlabStylelintMeta = {
meta: {
canonical: ["responsive-design"],
description: "Forbid the vh unit, which resolves to the largest viewport on mobile browsers",
fixable: false,
},
ruleId,
ruleName,
};
const LARGE_VIEWPORT_UNIT = "vh";
const messages = utils.ruleMessages(ruleName, {
largeViewport: (): string =>
withRuleId(
"The vh unit resolves to the largest viewport, the one with the browser toolbar hidden, so on a phone a vh height runs under the visible toolbar and cuts off the bottom of the page. Use dvh for a height that follows the toolbar, svh for a height that must stay fully visible, or lvh where the largest viewport is meant.",
ruleId,
),
});
const rule: Rule = Object.assign(
(primary: unknown) =>
(root: Root, result: PostcssResult): void => {
if (primary !== true) {
return;
}
root.walkDecls((decl: Declaration) => {
if (valueUnits(decl.value).some((token) => token.unit.toLowerCase() === LARGE_VIEWPORT_UNIT)) {
utils.report({ message: messages.largeViewport(), node: decl, result, ruleName, word: decl.value });
}
});
},
{ messages, ruleName },
);
export default createPlugin(ruleName, rule);