Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 391b546

Browse files
committedAug 17, 2022
feat(selector): add selector mixin
1 parent 1ad70ef commit 391b546

File tree

5 files changed

+323
-1
lines changed

5 files changed

+323
-1
lines changed
 

‎package-lock.json

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@
2929
"stylelint-config-unsass": "^1.0.0"
3030
},
3131
"dependencies": {
32+
"@unsass/utilities": "^1.3.1",
3233
"sass": "^1.52.3"
3334
},
3435
"keywords": [
3536
"sass",
36-
"css",
3737
"declaration",
38+
"custom properties",
3839
"unsass",
3940
"front-end"
4041
],

‎src/_functions.scss

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// ============================================================================================= //
2+
// MIXINS //
3+
// ============================================================================================= //
4+
5+
@use "sass:map";
6+
@use "sass:meta";
7+
8+
// ------------------------------------------------------------------------- //
9+
// Privates functions
10+
// ------------------------------------------------------------------------- //
11+
12+
// ...
13+
14+
// ------------------------------------------------------------------------- //
15+
// Public functions
16+
// ------------------------------------------------------------------------- //
17+
18+
///
19+
/// Number.
20+
///
21+
@function number($value) {
22+
@if meta.type-of($value) == "number" {
23+
@return $value;
24+
} @else if meta.type-of($value) != "string" {
25+
@error "Invalid string type for `#{$value}` value. Use string type instead.";
26+
}
27+
28+
$numbers: (
29+
"0": 0,
30+
"1": 1,
31+
"2": 2,
32+
"3": 3,
33+
"4": 4,
34+
"5": 5,
35+
"6": 6,
36+
"7": 7,
37+
"8": 8,
38+
"9": 9
39+
);
40+
41+
@return map.get($numbers, $value);
42+
}

‎src/_mixins.scss

+65
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
// MIXINS //
33
// ============================================================================================= //
44

5+
@use "sass:meta";
6+
@use "sass:list";
7+
@use "sass:string";
8+
@use "@unsass/utilities";
59
@use "./custom-properties";
10+
@use "./functions";
611

712
// ------------------------------------------------------------------------- //
813
// Privates mixins
@@ -67,3 +72,63 @@
6772
@include _declaration($property, $value, $important);
6873
}
6974
}
75+
76+
///
77+
/// @example - scss
78+
/// .foo {
79+
/// @include selector(md) {
80+
/// background: darkcyan;
81+
/// }
82+
/// }
83+
///
84+
/// @example - css
85+
/// .md:foo {
86+
/// background: darkcyan;
87+
/// }
88+
///
89+
/// @example - scss
90+
/// .foo {
91+
/// @include selector(md, $suffix: true) {
92+
/// background: darkcyan;
93+
/// }
94+
/// }
95+
///
96+
/// @example - css
97+
/// .foo:md {
98+
/// background: darkcyan;
99+
/// }
100+
///
101+
/// @see {function} functions.number
102+
/// @see {function} functions.replace
103+
///
104+
/// @link https://sparkbox.com/foundry/responsive_class_suffixes_automating_classes_with_sass_mixins_and_sass_maps
105+
///
106+
@mixin selector($key, $separator: ":", $suffix: false, $selector: &) {
107+
$string: if(meta.type-of($selector) == list, "#{list.nth($selector, 1)}", $selector);
108+
$is-class: string.slice($string, 1, 1);
109+
$nested-selector: if($selector == &, true, false);
110+
111+
@if $is-class != "." {
112+
@error "Invalid `#{$selector}` selector. Choose a class selector instead.";
113+
} @else {
114+
$selector: string.slice($string, 2);
115+
}
116+
117+
@if meta.type-of(functions.number(string.slice($key, 1, $end-at: 1))) == "number" {
118+
$key: utilities.string-replace(\3 #{string.unquote($key)}, " ");
119+
}
120+
121+
@if $suffix and $nested-selector {
122+
&#{string.unquote("\\") + $separator}#{$key} {
123+
@content;
124+
}
125+
} @else if $suffix and not $nested-selector {
126+
.#{$selector}#{string.unquote("\\") + $separator}#{$key} {
127+
@content;
128+
}
129+
} @else {
130+
@at-root .#{$key}#{string.unquote("\\") + $separator}#{$selector} {
131+
@content;
132+
}
133+
}
134+
}

‎tests/mixins.spec.scss

+193
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,196 @@
137137
}
138138
}
139139
}
140+
141+
@include describe("css.selector()") {
142+
@include it("Should return prefixed selector.") {
143+
@include assert {
144+
@include output(false) {
145+
.foo {
146+
@include css.selector("md") {
147+
color: darkcyan;
148+
}
149+
}
150+
}
151+
152+
@include expect(false) {
153+
.md\:foo {
154+
color: darkcyan;
155+
}
156+
}
157+
}
158+
}
159+
160+
@include it("Should return prefixed selector with custom separator.") {
161+
@include assert {
162+
@include output(false) {
163+
.foo {
164+
@include css.selector("md", "@") {
165+
color: darkcyan;
166+
}
167+
}
168+
}
169+
170+
@include expect(false) {
171+
.md\@foo {
172+
color: darkcyan;
173+
}
174+
}
175+
}
176+
}
177+
178+
179+
180+
@include it("Should return prefixed selector with numbers.") {
181+
@include assert {
182+
@include output(false) {
183+
.foo {
184+
@include css.selector("2xl") {
185+
color: darkcyan;
186+
}
187+
}
188+
}
189+
190+
@include expect(false) {
191+
.\32 xl\:foo {
192+
color: darkcyan;
193+
}
194+
}
195+
}
196+
}
197+
198+
@include it("Should return suffixed selector.") {
199+
@include assert {
200+
@include output(false) {
201+
.foo {
202+
@include css.selector("md", $suffix: true) {
203+
color: darkcyan;
204+
}
205+
}
206+
}
207+
208+
@include expect(false) {
209+
.foo\:md {
210+
color: darkcyan;
211+
}
212+
}
213+
}
214+
}
215+
216+
@include it("Should return suffixed selector with custom separator.") {
217+
@include assert {
218+
@include output(false) {
219+
.foo {
220+
@include css.selector("md", "@", true) {
221+
color: darkcyan;
222+
}
223+
}
224+
}
225+
226+
@include expect(false) {
227+
.foo\@md {
228+
color: darkcyan;
229+
}
230+
}
231+
}
232+
}
233+
234+
@include it("Should return suffixed selector with numbers.") {
235+
@include assert {
236+
@include output(false) {
237+
.foo {
238+
@include css.selector("2xl", $suffix: true) {
239+
color: darkcyan;
240+
}
241+
}
242+
}
243+
244+
@include expect(false) {
245+
.foo\:2xl {
246+
color: darkcyan;
247+
}
248+
}
249+
}
250+
}
251+
252+
@include it("Should return prefixed custom selector.") {
253+
@include assert {
254+
@include output(false) {
255+
@include css.selector("md", $selector: ".foo") {
256+
color: darkcyan;
257+
}
258+
}
259+
260+
@include expect(false) {
261+
.md\:foo {
262+
color: darkcyan;
263+
}
264+
}
265+
}
266+
}
267+
268+
@include it("Should return prefixed custom selector.") {
269+
@include assert {
270+
@include output(false) {
271+
@include css.selector("md", $selector: ".foo") {
272+
color: darkcyan;
273+
}
274+
}
275+
276+
@include expect(false) {
277+
.md\:foo {
278+
color: darkcyan;
279+
}
280+
}
281+
}
282+
}
283+
284+
@include it("Should return prefixed custom selector with custom separator.") {
285+
@include assert {
286+
@include output(false) {
287+
@include css.selector("md", "@", $selector: ".foo") {
288+
color: darkcyan;
289+
}
290+
}
291+
292+
@include expect(false) {
293+
.md\@foo {
294+
color: darkcyan;
295+
}
296+
}
297+
}
298+
}
299+
300+
@include it("Should return suffixed custom selector.") {
301+
@include assert {
302+
@include output(false) {
303+
@include css.selector("md", $suffix: true, $selector: ".foo") {
304+
color: darkcyan;
305+
}
306+
}
307+
308+
@include expect(false) {
309+
.foo\:md {
310+
color: darkcyan;
311+
}
312+
}
313+
}
314+
}
315+
316+
@include it("Should return suffixed custom selector with custom separator.") {
317+
@include assert {
318+
@include output(false) {
319+
@include css.selector("md", "@", true, ".foo") {
320+
color: darkcyan;
321+
}
322+
}
323+
324+
@include expect(false) {
325+
.foo\@md {
326+
color: darkcyan;
327+
}
328+
}
329+
}
330+
}
331+
332+
}

0 commit comments

Comments
 (0)
Please sign in to comment.