@unsass/selector 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,28 +2,28 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
4
4
 
5
- ## 1.4.0 (2026-01-03)
5
+ ## [1.6.0](https://github.com/unsass/selector/compare/v1.5.0...v1.6.0) (2026-09-19)
6
+
7
+ ### Dependencies
8
+
9
+ * **deps:** require node >=22 ([fc20214](https://github.com/unsass/selector/commit/fc20214fb8a6187cadadbf51c6d082aa9da25e0d))
10
+
11
+ ## [1.5.0](https://github.com/unsass/selector/compare/v1.4.0...v1.5.0) (2026-06-13)
12
+
13
+ ### Features
14
+
15
+ * add functional pseudo-class helpers ([09ee327](https://github.com/unsass/selector/commit/09ee32775dca2092b984ffacd0df7939d0eb4318))
16
+
17
+ ## [1.4.0](https://github.com/unsass/selector/compare/v1.3.0...v1.4.0) (2026-01-03)
18
+
19
+ ### Features
6
20
 
7
- * ci: add `release.config.js` ([6fb94ff](https://github.com/unsass/selector/commit/6fb94ff))
8
- * ci: lint code ([e7c5ae5](https://github.com/unsass/selector/commit/e7c5ae5))
9
- * ci: rename `node.js.yml` to `publish.yml` ([bdfb2a2](https://github.com/unsass/selector/commit/bdfb2a2))
10
- * ci: update `commit-msg` ([7f78c1c](https://github.com/unsass/selector/commit/7f78c1c))
11
21
  * feat: use the plain-CSS if() syntax ([bfda10e](https://github.com/unsass/selector/commit/bfda10e))
12
- * chore: switch to ESM ([faee4d8](https://github.com/unsass/selector/commit/faee4d8))
13
- * chore(deps-dev): bump to `@commitlint/cli` 20.3.0 ([581c39e](https://github.com/unsass/selector/commit/581c39e))
14
- * chore(deps-dev): bump to `sass-true` 10.1.0 ([125f7b4](https://github.com/unsass/selector/commit/125f7b4))
15
- * chore(deps-dev): bump to `stylelint` 16.26.1 ([9249a20](https://github.com/unsass/selector/commit/9249a20))
16
- * chore(deps-dev): prevent dependencies vulnerabilities ([311b5f1](https://github.com/unsass/selector/commit/311b5f1))
17
- * chore(deps): bump to `@commitlint/cli` 20.1.0 ([7873ab3](https://github.com/unsass/selector/commit/7873ab3))
18
- * chore(deps): bump to `sass-true` 8.1.0 ([796746d](https://github.com/unsass/selector/commit/796746d))
19
- * chore(deps): bump to `sass-true` 9.0.0 ([c6f302c](https://github.com/unsass/selector/commit/c6f302c))
22
+
23
+ ### Dependencies
24
+
20
25
  * chore(deps): bump to `sass` 1.97.1 ([3dfbf70](https://github.com/unsass/selector/commit/3dfbf70))
21
- * chore(deps): bump to `semantic-release` 25.0.1 ([93db310](https://github.com/unsass/selector/commit/93db310))
22
- * chore(deps): bump to `stylelint-config-unsass` 2.0.0 ([ccb922e](https://github.com/unsass/selector/commit/ccb922e))
23
- * chore(deps): bump to `stylelint-config-unsass` 2.2.0 ([04e20fa](https://github.com/unsass/selector/commit/04e20fa))
24
- * chore(deps): clean `package-lock.json` ([65d4a3d](https://github.com/unsass/selector/commit/65d4a3d))
25
- * chore(deps): prevent dependencies vulnerabilities ([b3fbab9](https://github.com/unsass/selector/commit/b3fbab9))
26
- * test(deps): switch from jest to vitest ([8c3ed03](https://github.com/unsass/selector/commit/8c3ed03))
26
+
27
27
 
28
28
  ## [1.3.0](https://github.com/unsass/selector/compare/v1.2.0...v1.3.0) (2023-09-11)
29
29
 
package/README.md CHANGED
@@ -6,7 +6,15 @@
6
6
 
7
7
  ## Introduction
8
8
 
9
- Sass functions and mixins to manage CSS selectors.
9
+ A small Sass toolkit for building CSS selectors programmatically. Compose scoped, prefixed or suffixed class names with
10
+ pseudo-classes, pseudo-elements and `@at-root` support through concise, composable mixins and helpers, so selector logic
11
+ stays readable and consistent.
12
+
13
+ <div align="center">
14
+
15
+ ![Selector](.github/banner.svg)
16
+
17
+ </div>
10
18
 
11
19
  ## Installing
12
20
 
@@ -24,7 +32,35 @@ npm install @unsass/selector
24
32
  }
25
33
  ```
26
34
 
27
- ### Result
35
+ ```css
36
+ .md\:foo {
37
+ color: darkcyan;
38
+ }
39
+ ```
40
+
41
+ ## Mixins
42
+
43
+ ### `create($selector, $scope, $separator, $suffix, $pseudo-class, $pseudo-element, $root)`
44
+
45
+ Builds a class selector from a name, with optional scope, separator, pseudo and `@at-root` options.
46
+
47
+ | Parameter | Description | Default |
48
+ |-------------------|------------------------------------------------------------|---------|
49
+ | `$selector` | Selector name (a leading `.` is stripped). | — |
50
+ | `$scope` | Scope affix value, as a single token or a list of tokens. | `null` |
51
+ | `$separator` | Scope affix separator. | `":"` |
52
+ | `$suffix` | Append the scope as a suffix instead of a prefix. | `false` |
53
+ | `$pseudo-class` | Pseudo-class appended to the selector. | `null` |
54
+ | `$pseudo-element` | Pseudo-element appended to the selector. | `null` |
55
+ | `$root` | Wrap the output in an `@at-root` rule. | `false` |
56
+
57
+ ```scss
58
+ @use "@unsass/selector";
59
+
60
+ @include selector.create("foo", "md") {
61
+ color: darkcyan;
62
+ }
63
+ ```
28
64
 
29
65
  ```css
30
66
  .md\:foo {
@@ -32,13 +68,25 @@ npm install @unsass/selector
32
68
  }
33
69
  ```
34
70
 
35
- ## API
71
+ #### `$scope`
36
72
 
37
- | Mixin | Description |
38
- |-----------------------------------------------------------------------------------------|------------------------------------------------------------|
39
- | `create($selector, $scope, $separator, $suffix, $pseudo-class, $pseudo-element, $root)` | Sets new CSS selector with class scope and pseudo options. |
73
+ Pass a list to chain several scope tokens.
40
74
 
41
- ### `$separator`
75
+ ```scss
76
+ @use "@unsass/selector";
77
+
78
+ @include selector.create("foo", ("md", "lg")) {
79
+ color: darkcyan;
80
+ }
81
+ ```
82
+
83
+ ```css
84
+ .md\:lg\:foo {
85
+ color: darkcyan;
86
+ }
87
+ ```
88
+
89
+ #### `$separator`
42
90
 
43
91
  Define your own scope separator character.
44
92
 
@@ -50,17 +98,15 @@ Define your own scope separator character.
50
98
  }
51
99
  ```
52
100
 
53
- ### Result
54
-
55
101
  ```css
56
102
  .md\@foo {
57
103
  color: darkcyan;
58
104
  }
59
105
  ```
60
106
 
61
- ### `$suffix`
107
+ #### `$suffix`
62
108
 
63
- Define the scope value has a prefix on selector.
109
+ Append the scope value as a suffix on the selector.
64
110
 
65
111
  ```scss
66
112
  @use "@unsass/selector";
@@ -70,17 +116,15 @@ Define the scope value has a prefix on selector.
70
116
  }
71
117
  ```
72
118
 
73
- ### Result
74
-
75
119
  ```css
76
120
  .foo\:md {
77
121
  color: darkcyan;
78
122
  }
79
123
  ```
80
124
 
81
- ### `$pseudo-class`
125
+ #### `$pseudo-class`
82
126
 
83
- Define the pseudo class suffix.
127
+ Define the pseudo-class suffix.
84
128
 
85
129
  ```scss
86
130
  @use "@unsass/selector";
@@ -90,17 +134,15 @@ Define the pseudo class suffix.
90
134
  }
91
135
  ```
92
136
 
93
- ### Result
94
-
95
137
  ```css
96
138
  .hover\:foo:hover {
97
139
  color: darkcyan;
98
140
  }
99
141
  ```
100
142
 
101
- ### `$pseudo-element`
143
+ #### `$pseudo-element`
102
144
 
103
- Define the pseudo element suffix.
145
+ Define the pseudo-element suffix.
104
146
 
105
147
  ```scss
106
148
  @use "@unsass/selector";
@@ -110,17 +152,15 @@ Define the pseudo element suffix.
110
152
  }
111
153
  ```
112
154
 
113
- ### Result
114
-
115
155
  ```css
116
156
  .before\:foo::before {
117
157
  color: darkcyan;
118
158
  }
119
159
  ```
120
160
 
121
- ### `$root`
161
+ #### `$root`
122
162
 
123
- Wrap the selector with `@at-root` rule before code output.
163
+ Wrap the selector with an `@at-root` rule before code output.
124
164
 
125
165
  ```scss
126
166
  @use "@unsass/selector";
@@ -134,10 +174,129 @@ Wrap the selector with `@at-root` rule before code output.
134
174
  }
135
175
  ```
136
176
 
137
- ### Result
138
-
139
177
  ```css
140
178
  .md\:foo .bar {
141
179
  color: darkcyan;
142
180
  }
143
181
  ```
182
+
183
+ ### `media($query)`
184
+
185
+ Wraps the content in a `@media` rule.
186
+
187
+ ```scss
188
+ @use "@unsass/selector";
189
+
190
+ .foo {
191
+ @include selector.media("screen") {
192
+ color: darkcyan;
193
+ }
194
+ }
195
+ ```
196
+
197
+ ```css
198
+ @media screen {
199
+ .foo {
200
+ color: darkcyan;
201
+ }
202
+ }
203
+ ```
204
+
205
+ ## Functions
206
+
207
+ ### `to-class($name)`
208
+
209
+ Returns a class selector from a name.
210
+
211
+ ```scss
212
+ @use "@unsass/selector";
213
+
214
+ $selector: selector.to-class("foo"); // ".foo"
215
+ ```
216
+
217
+ ### `to-id($name)`
218
+
219
+ Returns an id selector from a name.
220
+
221
+ ```scss
222
+ @use "@unsass/selector";
223
+
224
+ $selector: selector.to-id("foo"); // "#foo"
225
+ ```
226
+
227
+ ### `pseudo-class($selector, $pseudo-class)`
228
+
229
+ Appends a pseudo-class to a selector.
230
+
231
+ ```scss
232
+ @use "@unsass/selector";
233
+
234
+ $selector: selector.pseudo-class(".foo", "hover"); // ".foo:hover"
235
+ ```
236
+
237
+ ### `pseudo-element($selector, $pseudo-element)`
238
+
239
+ Appends a pseudo-element to a selector.
240
+
241
+ ```scss
242
+ @use "@unsass/selector";
243
+
244
+ $selector: selector.pseudo-element(".foo", "before"); // ".foo::before"
245
+ ```
246
+
247
+ ### `is($selector, $args…)`
248
+
249
+ Restricts the selector with an `:is()` pseudo-class matching any of the given selectors.
250
+
251
+ ```scss
252
+ @use "@unsass/selector";
253
+
254
+ $selector: selector.is("article", "h1", "h2"); // "article:is(h1, h2)"
255
+ ```
256
+
257
+ ### `where($selector, $args…)`
258
+
259
+ Like `is()`, but with zero specificity through the `:where()` pseudo-class.
260
+
261
+ ```scss
262
+ @use "@unsass/selector";
263
+
264
+ $selector: selector.where(".prose", "a", "button"); // ".prose:where(a, button)"
265
+ ```
266
+
267
+ ### `has($selector, $args…)`
268
+
269
+ Restricts the selector to elements matching the relational `:has()` condition.
270
+
271
+ ```scss
272
+ @use "@unsass/selector";
273
+
274
+ $selector: selector.has(".card", "> img"); // ".card:has(> img)"
275
+ ```
276
+
277
+ ### `exclude($selector, $args…)`
278
+
279
+ Excludes the given selectors through a `:not()` pseudo-class. Named `exclude` because `not` is a reserved Sass keyword.
280
+
281
+ ```scss
282
+ @use "@unsass/selector";
283
+
284
+ $selector: selector.exclude(".btn", ".btn--disabled"); // ".btn:not(.btn--disabled)"
285
+ $selector: selector.exclude(".item", ".a", ".b"); // ".item:not(.a, .b)"
286
+ ```
287
+
288
+ These helpers return selectors, so they compose:
289
+
290
+ ```scss
291
+ @use "@unsass/selector";
292
+
293
+ #{selector.exclude(selector.is(".x", "a", "b"), ".y")} {
294
+ color: darkcyan;
295
+ }
296
+ ```
297
+
298
+ ```css
299
+ .x:is(a, b):not(.y) {
300
+ color: darkcyan;
301
+ }
302
+ ```
package/package.json CHANGED
@@ -1,44 +1,54 @@
1
1
  {
2
2
  "name": "@unsass/selector",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "Sass functions and mixins to manage CSS selectors.",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "lint:css": "stylelint --fix \"**/*.scss\"",
8
8
  "lint": "npm-run-all --parallel lint:*",
9
9
  "test": "vitest",
10
- "prepare": "husky install",
10
+ "prepare": "husky",
11
11
  "release": "semantic-release"
12
12
  },
13
13
  "engines": {
14
- "node": ">=18"
14
+ "node": ">=22"
15
15
  },
16
16
  "sass": "index.scss",
17
17
  "devDependencies": {
18
- "@commitlint/cli": "^20.3.0",
19
- "@commitlint/config-conventional": "^20.3.0",
20
- "@semantic-release/changelog": "^6.0.3",
21
- "@semantic-release/git": "^10.0.1",
18
+ "@commitlint/cli": "^21.2.2",
19
+ "@commitlint/config-conventional": "^21.2.2",
20
+ "@semantic-release/changelog": "^7.0.0",
21
+ "@semantic-release/git": "^11.0.1",
22
+ "@semantic-release/github": "^12.0.9",
23
+ "conventional-changelog-conventionalcommits": "^9.3.1",
22
24
  "husky": "^9.1.7",
23
25
  "npm-run-all": "^4.1.5",
26
+ "sass": "^1.104.1",
24
27
  "sass-true": "^10.1.0",
25
- "semantic-release": "^25.0.2",
28
+ "semantic-release": "^25.0.9",
26
29
  "stylelint": "^16.26.1",
27
- "stylelint-config-unsass": "^2.2.0",
28
- "vitest": "^4.0.16"
30
+ "stylelint-config-unsass": "^2.3.0",
31
+ "vitest": "^5.0.1"
29
32
  },
30
33
  "dependencies": {
31
34
  "@unsass/string": "^1.4.2",
32
- "@unsass/types": "^1.0.2",
33
- "sass": "^1.97.1"
35
+ "@unsass/types": "^1.0.2"
36
+ },
37
+ "peerDependencies": {
38
+ "sass": ">=1.95.0"
34
39
  },
35
40
  "keywords": [
36
41
  "sass",
42
+ "scss",
37
43
  "selector",
38
44
  "class",
45
+ "pseudo",
46
+ "mixins",
47
+ "functions",
39
48
  "unsass",
40
49
  "front-end"
41
50
  ],
51
+ "author": "Front Factory",
42
52
  "license": "MIT",
43
53
  "homepage": "https://github.com/unsass/selector#readme",
44
54
  "bugs": {
@@ -54,6 +64,7 @@
54
64
  "CHANGELOG.md"
55
65
  ],
56
66
  "publishConfig": {
57
- "access": "public"
67
+ "access": "public",
68
+ "provenance": true
58
69
  }
59
70
  }
@@ -1,5 +1,5 @@
1
1
  // ============================================================================================= //
2
- // MIXINS //
2
+ // FUNCTIONS //
3
3
  // ============================================================================================= //
4
4
 
5
5
  @use "sass:map";
@@ -21,6 +21,11 @@
21
21
  /// $selector: pseudo-class(".foo", "hover");
22
22
  /// // ".foo:hover"
23
23
  ///
24
+ /// @param {string} $selector - Base selector.
25
+ /// @param {string} $pseudo-class - Pseudo-class name (without the leading colon).
26
+ ///
27
+ /// @return {string} The selector suffixed with the pseudo-class.
28
+ ///
24
29
  /// @access public
25
30
  ///
26
31
  @function pseudo-class($selector, $pseudo-class) {
@@ -29,24 +34,99 @@
29
34
 
30
35
  ///
31
36
  /// @example - scss
32
- /// $selector: pseudo-class(".foo", "before");
37
+ /// $selector: pseudo-element(".foo", "before");
33
38
  /// // ".foo::before"
34
39
  ///
40
+ /// @param {string} $selector - Base selector.
41
+ /// @param {string} $pseudo-element - Pseudo-element name (without the leading colons).
42
+ ///
43
+ /// @return {string} The selector suffixed with the pseudo-element.
44
+ ///
35
45
  /// @access public
36
46
  ///
37
47
  @function pseudo-element($selector, $pseudo-element) {
38
48
  @return selector.append($selector, "::#{$pseudo-element}");
39
49
  }
40
50
 
51
+ ///
52
+ /// @example - scss
53
+ /// $selector: is("article", "h1", "h2");
54
+ /// // "article:is(h1, h2)"
55
+ ///
56
+ /// @param {string} $selector - Base selector.
57
+ /// @param {arglist} $args - One or more selectors to match.
58
+ ///
59
+ /// @return {selector} The selector restricted to the `:is()` matches.
60
+ ///
61
+ /// @access public
62
+ ///
63
+ @function is($selector, $args...) {
64
+ @return selector.append($selector, ":is(#{$args})");
65
+ }
66
+
67
+ ///
68
+ /// @example - scss
69
+ /// $selector: where(".prose", "a", "button");
70
+ /// // ".prose:where(a, button)"
71
+ ///
72
+ /// @param {string} $selector - Base selector.
73
+ /// @param {arglist} $args - One or more selectors to match (with zero specificity).
74
+ ///
75
+ /// @return {selector} The selector restricted to the `:where()` matches.
76
+ ///
77
+ /// @access public
78
+ ///
79
+ @function where($selector, $args...) {
80
+ @return selector.append($selector, ":where(#{$args})");
81
+ }
82
+
83
+ ///
84
+ /// @example - scss
85
+ /// $selector: has(".card", "> img");
86
+ /// // ".card:has(> img)"
87
+ ///
88
+ /// @param {string} $selector - Base selector.
89
+ /// @param {arglist} $args - One or more relative selectors the element must contain.
90
+ ///
91
+ /// @return {selector} The selector restricted to elements matching the `:has()` condition.
92
+ ///
93
+ /// @access public
94
+ ///
95
+ @function has($selector, $args...) {
96
+ @return selector.append($selector, ":has(#{$args})");
97
+ }
98
+
99
+ ///
100
+ /// Negation pseudo-class. Outputs the CSS `:not()` (renamed because `not` is a reserved Sass keyword).
101
+ ///
102
+ /// @example - scss
103
+ /// $selector: exclude(".btn", ".btn--disabled");
104
+ /// // ".btn:not(.btn--disabled)"
105
+ ///
106
+ /// @param {string} $selector - Base selector.
107
+ /// @param {arglist} $args - One or more selectors to exclude.
108
+ ///
109
+ /// @return {selector} The selector excluding the `:not()` matches.
110
+ ///
111
+ /// @access public
112
+ ///
113
+ @function exclude($selector, $args...) {
114
+ @return selector.append($selector, ":not(#{$args})");
115
+ }
116
+
41
117
  ///
42
118
  /// @example - scss
43
119
  /// $selector: to-id("foo");
44
120
  /// // "#foo"
45
121
  ///
122
+ /// @param {string} $name - Selector name (without the leading `#`).
123
+ ///
124
+ /// @return {string} The id selector.
125
+ ///
46
126
  /// @access public
47
127
  ///
48
- @function to-id($selector) {
49
- @return "##{$selector}";
128
+ @function to-id($name) {
129
+ @return "##{$name}";
50
130
  }
51
131
 
52
132
  ///
@@ -54,8 +134,12 @@
54
134
  /// $selector: to-class("foo");
55
135
  /// // ".foo"
56
136
  ///
137
+ /// @param {string} $name - Selector name (without the leading `.`).
138
+ ///
139
+ /// @return {string} The class selector.
140
+ ///
57
141
  /// @access public
58
142
  ///
59
- @function to-class($selector) {
60
- @return ".#{$selector}";
143
+ @function to-class($name) {
144
+ @return ".#{$name}";
61
145
  }
package/src/_mixins.scss CHANGED
@@ -25,7 +25,7 @@
25
25
  /// }
26
26
  ///
27
27
  /// @example - css
28
- /// .md:foo {
28
+ /// .md\:foo {
29
29
  /// color: darkcyan;
30
30
  /// }
31
31
  ///
@@ -50,11 +50,12 @@
50
50
  /// }
51
51
  ///
52
52
  /// @param {string} $selector - Selector name.
53
- /// @param {string, list} $scope - Selector affix value.
54
- /// @param {string} $separator - Selector affix separator.
55
- /// @param {string} $pseudo-class - Selector pseudo-class.
56
- /// @param {string} $pseudo-element - Selector pseudo-element.
57
- /// @param {boolean} $suffix - Use suffix instead of prefix.
53
+ /// @param {string | list} $scope [null] - Selector affix value.
54
+ /// @param {string} $separator [":"] - Selector affix separator.
55
+ /// @param {boolean} $suffix [false] - Use suffix instead of prefix.
56
+ /// @param {string} $pseudo-class [null] - Selector pseudo-class.
57
+ /// @param {string} $pseudo-element [null] - Selector pseudo-element.
58
+ /// @param {boolean} $root [false] - Wrap the output in an `@at-root` rule.
58
59
  ///
59
60
  /// @see {function} functions.pseudo-class
60
61
  /// @see {function} functions.pseudo-element
@@ -157,10 +158,12 @@
157
158
  /// }
158
159
  /// }
159
160
  ///
161
+ /// @param {string} $query - The media query condition.
162
+ ///
160
163
  /// @access public
161
164
  ///
162
- @mixin media($args) {
163
- @media #{$args} {
165
+ @mixin media($query) {
166
+ @media #{$query} {
164
167
  @content;
165
168
  }
166
169
  }