| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455 |
- import stylelint from 'stylelint'
- const ruleName = 'overleaf/no-themed-vars-in-root'
- const messages = stylelint.utils.ruleMessages(ruleName, {
- rejected: (variable, prop) =>
- `Unexpected themed variable "${variable}" in the value of "${prop}" inside a ":root" block. ` +
- 'The "data-theme" attribute is set on <body>, not <html>, so a themed variable referenced at ' +
- '":root" resolves against <html> (always the dark default) and never follows light mode. Declare ' +
- 'this custom property on a descendant selector (e.g. the component container) so the themed ' +
- 'variable resolves within the themed subtree.',
- })
- // Matches var(--foo-themed), capturing the variable name.
- const THEMED_VAR_RE = /var\(\s*(--[\w-]*-themed)\s*[,)]/g
- /** @type {import('stylelint').Rule} */
- const rule = primary => {
- return (root, result) => {
- const validOptions = stylelint.utils.validateOptions(result, ruleName, {
- actual: primary,
- possible: [true, false],
- })
- if (!validOptions || !primary) {
- return
- }
- root.walkRules(node => {
- const targetsRoot = node.selector
- .split(',')
- .map(selector => selector.trim())
- .some(selector => selector === ':root')
- if (!targetsRoot) {
- return
- }
- node.walkDecls(decl => {
- for (const [, variable] of decl.value.matchAll(THEMED_VAR_RE)) {
- stylelint.utils.report({
- result,
- ruleName,
- node: decl,
- word: variable,
- message: messages.rejected(variable, decl.prop),
- })
- }
- })
- })
- }
- }
- rule.ruleName = ruleName
- rule.messages = messages
- export default stylelint.createPlugin(ruleName, rule)
|