diff --git a/__tests__/focusableAtRule.test.js b/__tests__/focusableAtRule.test.js new file mode 100644 index 000000000..51cdda213 --- /dev/null +++ b/__tests__/focusableAtRule.test.js @@ -0,0 +1,25 @@ +import postcss from 'postcss' +import plugin from '../src/lib/substituteFocusableAtRules' + +function run(input, opts = {}) { + return postcss([plugin(opts)]).process(input) +} + +test("it adds a focusable variant to each nested class definition", () => { + const input = ` + @focusable { + .banana { color: yellow; } + .chocolate { color: brown; } + } + ` + + const output = ` + .banana, .focus\\:banana:focus { color: yellow; } + .chocolate, .focus\\:chocolate:focus { color: brown; } + ` + + return run(input, {}).then(result => { + expect(result.css).toEqual(output) + expect(result.warnings().length).toBe(0) + }) +}) diff --git a/src/index.js b/src/index.js index e0c1f3d56..368080a46 100644 --- a/src/index.js +++ b/src/index.js @@ -12,6 +12,7 @@ import substituteResetAtRule from './lib/substituteResetAtRule' import evaluateTailwindFunctions from './lib/evaluateTailwindFunctions' import generateUtilities from './lib/generateUtilities' import substituteHoverableAtRules from './lib/substituteHoverableAtRules' +import substituteFocusableAtRules from './lib/substituteFocusableAtRules' import substituteResponsiveAtRules from './lib/substituteResponsiveAtRules' import substituteScreenAtRules from './lib/substituteScreenAtRules' import substituteClassApplyAtRules from './lib/substituteClassApplyAtRules' @@ -28,6 +29,7 @@ const plugin = postcss.plugin('tailwind', (options = {}) => { evaluateTailwindFunctions(config), generateUtilities(config), substituteHoverableAtRules(config), + substituteFocusableAtRules(config), substituteResponsiveAtRules(config), substituteScreenAtRules(config), substituteClassApplyAtRules(config), diff --git a/src/lib/substituteFocusableAtRules.js b/src/lib/substituteFocusableAtRules.js new file mode 100644 index 000000000..c12aa2b6c --- /dev/null +++ b/src/lib/substituteFocusableAtRules.js @@ -0,0 +1,23 @@ +import _ from 'lodash' +import postcss from 'postcss' +import cloneNodes from '../util/cloneNodes' + +export default function(options) { + return function(css) { + css.walkAtRules('focusable', atRule => { + + atRule.walkRules(rule => { + // Might be wise to error if the rule has multiple selectors, + // or weird compound selectors like .bg-blue>p>h1 + rule.selectors = [ + rule.selector, + `.focus\\:${rule.selector.slice(1)}:focus` + ] + }) + + atRule.before(cloneNodes(atRule.nodes)) + + atRule.remove() + }) + } +} diff --git a/src/util/focusable.js b/src/util/focusable.js new file mode 100644 index 000000000..34996202b --- /dev/null +++ b/src/util/focusable.js @@ -0,0 +1,8 @@ +import postcss from 'postcss' +import cloneNodes from './cloneNodes' + +export default function focusable(rules) { + return postcss.atRule({ + name: 'focusable', + }).append(cloneNodes(rules)) +}