Skip to content

Commit

Permalink
Remove text opacity CSS variables from ::marker (#8622)
Browse files Browse the repository at this point in the history
* Refactor

* Allow parallel variant fns to mutate the container

* Remove text color variable from marker pseudo class

wip

* Update changelog
  • Loading branch information
thecrypticace committed Jun 14, 2022
1 parent 22eaad1 commit 15dc5a3
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 32 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix missing spaces around arithmetic operators ([#8615](https://github.com/tailwindlabs/tailwindcss/pull/8615))
- Detect alpha value in CSS `theme()` function when using quotes ([#8625](https://github.com/tailwindlabs/tailwindcss/pull/8625))
- Fix "Maximum call stack size exceeded" bug ([#8636](https://github.com/tailwindlabs/tailwindcss/pull/8636))
- Allow functions returning parallel variants to mutate the container ([#8622](https://github.com/tailwindlabs/tailwindcss/pull/8622))
- Remove text opacity CSS variables from `::marker` ([#8622](https://github.com/tailwindlabs/tailwindcss/pull/8622))

## [3.1.2] - 2022-06-10

Expand Down
35 changes: 19 additions & 16 deletions src/corePlugins.js
Expand Up @@ -14,14 +14,27 @@ import { version as tailwindVersion } from '../package.json'
import log from './util/log'
import { normalizeScreens } from './util/normalizeScreens'
import { formatBoxShadowValue, parseBoxShadowValue } from './util/parseBoxShadowValue'
import { removeAlphaVariables } from './util/removeAlphaVariables'
import { flagEnabled } from './featureFlags'

export let variantPlugins = {
pseudoElementVariants: ({ addVariant }) => {
addVariant('first-letter', '&::first-letter')
addVariant('first-line', '&::first-line')

addVariant('marker', ['& *::marker', '&::marker'])
addVariant('marker', [
({ container }) => {
removeAlphaVariables(container, ['--tw-text-opacity'])

return '& *::marker'
},
({ container }) => {
removeAlphaVariables(container, ['--tw-text-opacity'])

return '&::marker'
},
])

addVariant('selection', ['& *::selection', '&::selection'])

addVariant('file', '&::file-selector-button')
Expand Down Expand Up @@ -77,21 +90,11 @@ export let variantPlugins = {
[
'visited',
({ container }) => {
let toRemove = ['--tw-text-opacity', '--tw-border-opacity', '--tw-bg-opacity']

container.walkDecls((decl) => {
if (toRemove.includes(decl.prop)) {
decl.remove()

return
}

for (const varName of toRemove) {
if (decl.value.includes(`/ var(${varName})`)) {
decl.value = decl.value.replace(`/ var(${varName})`, '')
}
}
})
removeAlphaVariables(container, [
'--tw-text-opacity',
'--tw-border-opacity',
'--tw-bg-opacity',
])

return '&:visited'
},
Expand Down
28 changes: 18 additions & 10 deletions src/lib/generateRules.js
Expand Up @@ -163,15 +163,17 @@ function applyVariant(variant, matches, context) {

let container = postcss.root({ nodes: [rule.clone()] })

for (let [variantSort, variantFunction] of variantFunctionTuples) {
let clone = container.clone()
for (let [variantSort, variantFunction, containerFromArray] of variantFunctionTuples) {
let clone = containerFromArray ?? container.clone()
let collectedFormats = []

let originals = new Map()

function prepareBackup() {
if (originals.size > 0) return // Already prepared, chicken out
clone.walkRules((rule) => originals.set(rule, rule.selector))
// Already prepared, chicken out
if (clone.raws.neededBackup) {
return
}
clone.raws.neededBackup = true
clone.walkRules((rule) => (rule.raws.originalSelector = rule.selector))
}

function modifySelectors(modifierFunction) {
Expand Down Expand Up @@ -231,6 +233,10 @@ function applyVariant(variant, matches, context) {
// reserving additional X places for these 'unknown' variants in between.
variantSort | BigInt(idx << ruleWithVariant.length),
variantFunction,

// If the clone has been modified we have to pass that back
// though so each rule can use the modified container
clone.clone(),
])
}
continue
Expand All @@ -244,13 +250,15 @@ function applyVariant(variant, matches, context) {
continue
}

// We filled the `originals`, therefore we assume that somebody touched
// We had to backup selectors, therefore we assume that somebody touched
// `container` or `modifySelectors`. Let's see if they did, so that we
// can restore the selectors, and collect the format strings.
if (originals.size > 0) {
if (clone.raws.neededBackup) {
delete clone.raws.neededBackup
clone.walkRules((rule) => {
if (!originals.has(rule)) return
let before = originals.get(rule)
let before = rule.raws.originalSelector
if (!before) return
delete rule.raws.originalSelector
if (before === rule.selector) return // No mutation happened

let modified = rule.selector
Expand Down
24 changes: 24 additions & 0 deletions src/util/removeAlphaVariables.js
@@ -0,0 +1,24 @@
/**
* This function removes any uses of CSS variables used as an alpha channel
*
* This is required for selectors like `:visited` which do not allow
* changes in opacity or external control using CSS variables.
*
* @param {import('postcss').Container} container
* @param {string[]} toRemove
*/
export function removeAlphaVariables(container, toRemove) {
container.walkDecls((decl) => {
if (toRemove.includes(decl.prop)) {
decl.remove()

return
}

for (let varName of toRemove) {
if (decl.value.includes(`/ var(${varName})`)) {
decl.value = decl.value.replace(`/ var(${varName})`, '')
}
}
})
}
49 changes: 49 additions & 0 deletions tests/parallel-variants.test.js
Expand Up @@ -85,3 +85,52 @@ test('parallel variants can be generated using a function that returns parallel
`)
})
})

test('a function that returns parallel variants can modify the container', async () => {
let config = {
content: [
{
raw: html`<div
class="hover:test:font-black test:font-bold test:font-medium font-normal"
></div>`,
},
],
plugins: [
function test({ addVariant }) {
addVariant('test', ({ container }) => {
container.walkDecls((decl) => {
decl.value = `calc(0 + ${decl.value})`
})

return ['& *::test', '&::test']
})
},
],
}

return run('@tailwind utilities', config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
.font-normal {
font-weight: 400;
}
.test\:font-bold *::test {
font-weight: calc(0 + 700);
}
.test\:font-medium *::test {
font-weight: calc(0 + 500);
}
.test\:font-bold::test {
font-weight: calc(0 + 700);
}
.test\:font-medium::test {
font-weight: calc(0 + 500);
}
.hover\:test\:font-black *:hover::test {
font-weight: calc(0 + 900);
}
.hover\:test\:font-black:hover::test {
font-weight: calc(0 + 900);
}
`)
})
})
6 changes: 2 additions & 4 deletions tests/variants.test.css
Expand Up @@ -112,16 +112,14 @@
line-height: 1.75rem;
}
.marker\:text-red-500 *::marker {
--tw-text-opacity: 1;
color: rgb(239 68 68 / var(--tw-text-opacity));
color: rgb(239 68 68);
}
.marker\:text-lg::marker {
font-size: 1.125rem;
line-height: 1.75rem;
}
.marker\:text-red-500::marker {
--tw-text-opacity: 1;
color: rgb(239 68 68 / var(--tw-text-opacity));
color: rgb(239 68 68);
}
.selection\:bg-blue-500 *::selection {
--tw-bg-opacity: 1;
Expand Down
3 changes: 1 addition & 2 deletions tests/variants.test.js
Expand Up @@ -485,8 +485,7 @@ test('returning non-strings and non-selectors in addVariant', () => {

addVariant('peer-aria-expanded-2', ({ modifySelectors, separator }) => {
let nodes = modifySelectors(
({ className }) =>
`.peer[aria-expanded="false"] ~ .${e(`peer-aria-expanded${separator}${className}`)}`
({ className }) => `.${e(`peer-aria-expanded-2${separator}${className}`)}`
)

return [
Expand Down

0 comments on commit 15dc5a3

Please sign in to comment.