Merge pull request #232 from tailwindcss/variants-rule

Add `@variants` at-rule for generating multiple variants
This commit is contained in:
Adam Wathan 2017-11-23 11:15:02 -05:00 committed by GitHub
commit 9c72969b7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 4938 additions and 1203 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,96 @@
import postcss from 'postcss'
import plugin from '../src/lib/substituteVariantsAtRules'
function run(input, opts = () => {}) {
return postcss([plugin(opts)]).process(input)
}
test('it can generate hover variants', () => {
const input = `
@variants hover {
.banana { color: yellow; }
.chocolate { color: brown; }
}
`
const output = `
.banana { color: yellow; }
.chocolate { color: brown; }
.hover\\:banana:hover { color: yellow; }
.hover\\:chocolate:hover { color: brown; }
`
return run(input).then(result => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})
test('it can generate focus variants', () => {
const input = `
@variants focus {
.banana { color: yellow; }
.chocolate { color: brown; }
}
`
const output = `
.banana { color: yellow; }
.chocolate { color: brown; }
.focus\\:banana:focus { color: yellow; }
.focus\\:chocolate:focus { color: brown; }
`
return run(input).then(result => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})
test('it can generate hover and focus variants', () => {
const input = `
@variants hover, focus {
.banana { color: yellow; }
.chocolate { color: brown; }
}
`
const output = `
.banana { color: yellow; }
.chocolate { color: brown; }
.focus\\:banana:focus { color: yellow; }
.focus\\:chocolate:focus { color: brown; }
.hover\\:banana:hover { color: yellow; }
.hover\\:chocolate:hover { color: brown; }
`
return run(input).then(result => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})
test('it wraps the output in a responsive at-rule if responsive is included as a variant', () => {
const input = `
@variants responsive, hover, focus {
.banana { color: yellow; }
.chocolate { color: brown; }
}
`
const output = `
@responsive {
.banana { color: yellow; }
.chocolate { color: brown; }
.focus\\:banana:focus { color: yellow; }
.focus\\:chocolate:focus { color: brown; }
.hover\\:banana:hover { color: yellow; }
.hover\\:chocolate:hover { color: brown; }
}
`
return run(input).then(result => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})

View File

@ -6,17 +6,16 @@ expect.extend({
return str.replace(/\s/g, '')
}
if (stripped(received) == stripped(argument)) {
if (stripped(received) === stripped(argument)) {
return {
message: () =>
`expected ${received} not to match CSS ${argument}`,
message: () => `expected ${received} not to match CSS ${argument}`,
pass: true,
};
}
} else {
return {
message: () => `expected ${received} to match CSS ${argument}`,
pass: false,
};
}
}
}
},
})

View File

@ -1,5 +1,3 @@
import cloneNodes from '../util/cloneNodes'
export default function() {
return function(css) {
css.walkAtRules('hoverable', atRule => {

View File

@ -0,0 +1,47 @@
import _ from 'lodash'
import postcss from 'postcss'
const variantGenerators = {
hover: container => {
const cloned = container.clone()
cloned.walkRules(rule => {
rule.selector = `.hover\\:${rule.selector.slice(1)}:hover`
})
return cloned.nodes
},
focus: container => {
const cloned = container.clone()
cloned.walkRules(rule => {
rule.selector = `.focus\\:${rule.selector.slice(1)}:focus`
})
return cloned.nodes
},
}
export default function() {
return function(css) {
css.walkAtRules('variants', atRule => {
const variants = postcss.list.comma(atRule.params)
if (variants.includes('responsive')) {
const responsiveParent = postcss.atRule({ name: 'responsive' })
atRule.before(responsiveParent)
responsiveParent.append(atRule)
}
atRule.before(atRule.clone().nodes)
_.forEach(['focus', 'hover'], variant => {
if (variants.includes(variant)) {
atRule.before(variantGenerators[variant](atRule))
}
})
atRule.remove()
})
}
}