Skip to content

css-has-pseudo : specificity and hover #128

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion experimental/css-has-pseudo/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Changes to CSS Has Pseudo

### Unreleased
### 0.1.1 (January 5, 2022)

- Added : support for id and tag selector specificity.
- Changed : hover tracking now also uses `leave` events for correct style re-calcs.

### 0.1.0 (January 4, 2022)

Tracking initial implementation of `:has()` pseudo-class in Safari Technology Preview.
This is a breaking change and affects both the generated CSS and the client side polyfill.
Expand Down
4 changes: 2 additions & 2 deletions experimental/css-has-pseudo/README-BROWSER.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ ECMA Script:

[git-img]: https://img.shields.io/badge/support-chat-blue.svg
[git-url]: https://gitter.im/postcss/postcss
[npm-img]: https://img.shields.io/npm/v/css-has-pseudo.svg
[npm-url]: https://www.npmjs.com/package/css-has-pseudo
[npm-img]: https://img.shields.io/npm/v/@csstools/css-has-pseudo-experimental.svg
[npm-url]: https://www.npmjs.com/package/@csstools/css-has-pseudo-experimental

[EXPERIMENTAL CSS Has Pseudo]: https://github.com/csstools/postcss-plugins/tree/main/experimental/css-has-pseudo
[Selectors Level 4]: https://drafts.csswg.org/selectors-4/#has
4 changes: 2 additions & 2 deletions experimental/css-has-pseudo/README-POSTCSS.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ plugins: [

[git-img]: https://img.shields.io/badge/support-chat-blue.svg
[git-url]: https://gitter.im/postcss/postcss
[npm-img]: https://img.shields.io/npm/v/css-has-pseudo.svg
[npm-url]: https://www.npmjs.com/package/css-has-pseudo
[npm-img]: https://img.shields.io/npm/v/@csstools/css-has-pseudo-experimental.svg
[npm-url]: https://www.npmjs.com/package/@csstools/css-has-pseudo-experimental

[PostCSS]: https://github.com/postcss/postcss
[EXPERIMENTAL CSS Has Pseudo]: https://github.com/csstools/postcss-plugins/tree/main/experimental/css-has-pseudo
Expand Down
4 changes: 2 additions & 2 deletions experimental/css-has-pseudo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ plugins: [

[git-img]: https://img.shields.io/badge/support-chat-blue.svg
[git-url]: https://gitter.im/postcss/postcss
[npm-img]: https://img.shields.io/npm/v/css-has-pseudo.svg
[npm-url]: https://www.npmjs.com/package/css-has-pseudo
[npm-img]: https://img.shields.io/npm/v/@csstools/css-has-pseudo-experimental.svg
[npm-url]: https://www.npmjs.com/package/@csstools/css-has-pseudo-experimental

[EXPERIMENTAL CSS Has Pseudo]: https://github.com/csstools/postcss-plugins/tree/main/experimental/css-has-pseudo
[Mutation Observer polyfill]: https://github.com/webmodules/mutation-observer
Expand Down
4 changes: 4 additions & 0 deletions experimental/css-has-pseudo/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ Can we safely use `:not(#does-not-exist)`/`:not(does-not-exist)` as prefixes for

see : https://drafts.csswg.org/selectors-4/#specificity-rules

### Update 1 :

This has been implemented.
Still needs to be documented.

## Plugin order :

Expand Down
18 changes: 10 additions & 8 deletions experimental/css-has-pseudo/src/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ export default function cssHasPseudo(document, options) {
document.addEventListener('input', transformObservedItemsThrottled);
document.addEventListener('change', transformObservedItemsThrottled, true);

if (options.hover) {
if ('onpointerenter' in document) {
document.addEventListener('pointerenter', transformObservedItemsThrottled, true);
document.addEventListener('pointerleave', transformObservedItemsThrottled, true);
} else {
document.addEventListener('mouseover', transformObservedItemsThrottled, true);
document.addEventListener('mouseout', transformObservedItemsThrottled, true);
}
}

// observe Javascript setters that effect pseudo-selectors
if ('defineProperty' in Object && 'getOwnPropertyDescriptor' in Object && 'hasOwnProperty' in Object) {
try {
Expand Down Expand Up @@ -144,14 +154,6 @@ export default function cssHasPseudo(document, options) {
}
}

if (options.hover) {
if ('onpointerenter' in document) {
document.addEventListener('pointerenter', transformObservedItemsThrottled, true);
} else {
document.addEventListener('mouseover', transformObservedItemsThrottled, true);
}
}

let transformObservedItemsThrottledBusy = false;
function transformObservedItemsThrottled() {
if (transformObservedItemsThrottledBusy) {
Expand Down
33 changes: 24 additions & 9 deletions experimental/css-has-pseudo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ const creator = (/** @type {{ preserve: true | false }} */ opts) => {

/** Whether the original rule should be preserved. */
const shouldPreserve = Boolean('preserve' in opts ? opts.preserve : true);
const doesNotExistName = opts.doesNotExistName ?? 'does-not-exist';

const doesNotExistId = ':not(#' + doesNotExistName + ')';
const doesNotExistClass = ':not(.' + doesNotExistName + ')';
const doesNotExistTag = ':not(' + doesNotExistName + ')';

return {
postcssPlugin: 'css-has-pseudo-experimental',
Expand All @@ -19,8 +24,6 @@ const creator = (/** @type {{ preserve: true | false }} */ opts) => {
return selector;
}

let specificity = 1;

let selectorAST;
try {
selectorAST = parser().astSync(selector);
Expand All @@ -42,16 +45,22 @@ const creator = (/** @type {{ preserve: true | false }} */ opts) => {
return selector;
}

const encodedSelector = '[' + encodeCSS(selector) + ']';
const abcSpecificity = selectorSpecificity(selectorAST);
specificity = Math.max(1, abcSpecificity.b);

let encodedSelectorWithBSpecificty = '';
const encodedSelector = '[' + encodeCSS(selector) + ']';
for (let i = 0; i < specificity; i++) {
encodedSelectorWithBSpecificty += encodedSelector;
let encodedSelectorWithSpecificity = encodedSelector;
for (let i = 0; i < abcSpecificity.a; i++) {
encodedSelectorWithSpecificity += doesNotExistId;
}
const bSpecificity = Math.max(1, abcSpecificity.b) - 1;
for (let i = 0; i < bSpecificity; i++) {
encodedSelectorWithSpecificity += doesNotExistClass;
}
for (let i = 0; i < abcSpecificity.c; i++) {
encodedSelectorWithSpecificity += doesNotExistTag;
}

return encodedSelectorWithBSpecificty;
return encodedSelectorWithSpecificity;
});

if (selectors.join(',') === rule.selectors.join(',')) {
Expand Down Expand Up @@ -79,7 +88,13 @@ function selectorSpecificity(node) {
let b = 0;
let c = 0;

if (node.type === 'id') {
if (node.type == 'universal') {
return {
a: 0,
b: 0,
c: 0,
};
} else if (node.type === 'id') {
a += 1;
} else if (node.type === 'tag') {
c += 1;
Expand Down
2 changes: 1 addition & 1 deletion experimental/css-has-pseudo/test/_browser.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
<link rel="stylesheet" href="/test/browser.expect.css">
<script src="/dist/browser-global.js"></script>
<script>cssHasPseudo(document, { observedAttributes: ['attrname'], debug: true });</script>
<script>cssHasPseudo(document, { observedAttributes: ['attrname'], debug: true, hover: true, forcePolyfill: true });</script>
</head>
<body>
<the-fixture id="fixture"></the-fixture>
Expand Down
4 changes: 4 additions & 0 deletions experimental/css-has-pseudo/test/basic.css
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,7 @@ body:not(:has(:focus)) {
.a, .x:has(> .b) {
order: 29;
}

.x:has(> .b *) {
order: 30;
}
Loading