Skip to content

Commit

Permalink
fix: focusable, last-child, spinner rules (#110)
Browse files Browse the repository at this point in the history
  • Loading branch information
AnnaRybkina committed Jun 1, 2023
1 parent 46170cf commit 40e75cf
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 5 deletions.
16 changes: 16 additions & 0 deletions src/_rules/animations.js
@@ -1,13 +1,29 @@
import { escapeSelector } from '@unocss/core';

/*----LOADING----*/
// Very hardcoded styling for loading, not decided if it will be themable so let's have this for now and change if applicable later
const inProgress = 'background-image:linear-gradient(135deg,rgba(0, 0, 0, 0.05) 25%,transparent 0,transparent 50%,rgba(0, 0, 0, 0.05) 0,rgba(0, 0, 0, 0.05) 75%,transparent 0,transparent) !important;background-size: 30px 30px;animation: animate-inprogress 3s linear infinite;';
const keyFrames = '@keyframes animate-inprogress {0% {background-position: 0 0;}to {background-position: 60px 0;}}';

/*----SPINNER----*/
const spinner = '--spinner-size: 24px;height: var(--spinner-size);width: var(--spinner-size);border-radius: 50%;border: calc(var(--spinner-size) / 8) solid var(--w-color-spinner-border);border-top-color: var(--w-color-spinner-border-top);position: relative;animation: animate-spinner 0.75s infinite linear;';
const spinnerPseudo = `content: ' ';height: calc(var(--spinner-size) / 8);width: calc(var(--spinner-size) / 8);border-radius: 50%;position: absolute;top: calc(var(--spinner-size) / 180);`;
const spinnerBefore = 'left:0;';
const spinnerAfter = 'right:0;';
const spinnerKeyframes = '@keyframes animate-spinner {to{transform:rotate(359deg);}}';

export const animations = [
[/^animate-inprogress$/, ([_selector]) => {
const selector = escapeSelector(_selector);
const base = `.${selector}{${inProgress}}`;
return base + keyFrames;
}],
[/^animate-spinner$/, ([_selector]) => {
const selector = escapeSelector(_selector);
const baseSpinner = `.${selector}{${spinner}}`;
const pseudo = `.${selector}::before,.${selector}::after{${spinnerPseudo}}`;
const pseudoAfter = `.${selector}::after{${spinnerAfter}}`;
const pseudoBefore = `.${selector}::before{${spinnerBefore}}`;
return baseSpinner + pseudo + pseudoAfter + pseudoBefore + spinnerKeyframes;
}],
];
29 changes: 24 additions & 5 deletions src/_rules/focus-ring.js
@@ -1,4 +1,4 @@
import { entriesToCss } from '@unocss/core';
import { entriesToCss, escapeSelector } from '@unocss/core';

// TODO: use actual variables and values when those has been defined
const focusRingStyle = entriesToCss(Object.entries({
Expand All @@ -14,11 +14,30 @@ const focusRingInsetStyle = {
'--w-outline-offset': '-3px',
};

const combinatorsByTag = {
'group': ' ',
'peer': '~',
'parent': '>',
'previous': '+',
};

export const focusRing = [
[/^focusable$/, ([selector]) => {
const focus = `.${selector}:focus,.${selector}:focus-visible{${focusRingStyle}}`;
const notFocusVisible = `.${selector}:not(:focus-visible){${outlineNone}}`;
return focus + notFocusVisible;
[/focusable$/, ([], { rawSelector: selectorWithVariant, currentSelector }) => {
if (currentSelector !== selectorWithVariant) {
const escapedSelector = escapeSelector(selectorWithVariant);
const tagLabel = selectorWithVariant?.split('-')?.[0] ?? '';
const combinator = combinatorsByTag[tagLabel];
if (!!combinator) {
const focus = `.${tagLabel}:focus${combinator}.${escapedSelector},.${tagLabel}:focus-visible${combinator}.${escapedSelector}{${focusRingStyle}}`;
const notFocusVisible = `.${tagLabel}:not(:focus-visible)${combinator}.${escapedSelector}{${outlineNone}}`;
return focus + notFocusVisible;
}
return `.${escapedSelector}:${selectorWithVariant.split(':')?.[0]}{${focusRingStyle}}`;
} else {
const focus = `.${currentSelector}:focus,.${currentSelector}:focus-visible{${focusRingStyle}}`;
const notFocusVisible = `.${currentSelector}:not(:focus-visible){${outlineNone}}`;
return focus + notFocusVisible;
}
}],
["focusable-inset", { ... focusRingInsetStyle }],
];
3 changes: 3 additions & 0 deletions src/_variants/index.js
Expand Up @@ -7,8 +7,10 @@ import {
variantPseudoClassesAndElements,
} from '@unocss/preset-mini/variants';
import { variantSpaceAndDivide } from './spaceAndDivide.js';
import { variantLastChild } from './lastChild.js';

export const variants = [
variantLastChild,
variantBreakpoints(),
variantImportant(),
variantNegative,
Expand All @@ -19,6 +21,7 @@ export const variants = [
];

export {
variantLastChild,
variantBreakpoints,
variantImportant,
variantNegative,
Expand Down
7 changes: 7 additions & 0 deletions src/_variants/lastChild.js
@@ -0,0 +1,7 @@
export const variantLastChild = (matcher) => {
if (!matcher?.startsWith('last-child:')) { return matcher; };
return {
matcher: matcher.slice(11),
selector: () => `.last-child\\:mb-0>:last-child`,
};
};
5 changes: 5 additions & 0 deletions test/__snapshots__/variants.js.snap
Expand Up @@ -13,6 +13,11 @@ exports[`variants > breakpoints 1`] = `
}"
`;

exports[`variants > custom 1`] = `
"/* layer: default */
.last-child\\\\:mb-0>:last-child{margin-bottom:0rem;}"
`;

exports[`variants > important 1`] = `
"/* layer: default */
.\\\\!mb-32{margin-bottom:3.2rem !important;}
Expand Down
9 changes: 9 additions & 0 deletions test/animations.js
Expand Up @@ -13,4 +13,13 @@ test('animate inprogress', async ({ uno }) => {
.animate-inprogress{background-image:linear-gradient(135deg,rgba(0, 0, 0, 0.05) 25%,transparent 0,transparent 50%,rgba(0, 0, 0, 0.05) 0,rgba(0, 0, 0, 0.05) 75%,transparent 0,transparent) !important;background-size: 30px 30px;animation: animate-inprogress 3s linear infinite;}@keyframes animate-inprogress {0% {background-position: 0 0;}to {background-position: 60px 0;}}"
`);
});
test('animate spinner', async ({ uno }) => {
const classes = ['animate-spinner'];

const { css } = await uno.generate(classes);
expect(css).toMatchInlineSnapshot(`
"/* layer: default */
.animate-spinner{--spinner-size: 24px;height: var(--spinner-size);width: var(--spinner-size);border-radius: 50%;border: calc(var(--spinner-size) / 8) solid var(--w-color-spinner-border);border-top-color: var(--w-color-spinner-border-top);position: relative;animation: animate-spinner 0.75s infinite linear;}.animate-spinner::before,.animate-spinner::after{content: ' ';height: calc(var(--spinner-size) / 8);width: calc(var(--spinner-size) / 8);border-radius: 50%;position: absolute;top: calc(var(--spinner-size) / 180);}.animate-spinner::after{right:0;}.animate-spinner::before{left:0;}@keyframes animate-spinner {to{transform:rotate(359deg);}}"
`);
});

6 changes: 6 additions & 0 deletions test/focus-ring.js
Expand Up @@ -6,14 +6,20 @@ setup();
test("focus ring", async (t) => {
const classes = [
"focusable",
"peer-focus:focusable",
"group-focus:focusable",
"focus-within:focusable",
"focusable-inset",
];

const { css } = await t.uno.generate(classes);

expect(css).toMatchInlineSnapshot(`
"/* layer: default */
.focus-within\\\\:focusable:focus-within{outline:2px solid var(--w-color-focused);outline-offset:var(--w-outline-offset, 1px);}
.focusable:focus,.focusable:focus-visible{outline:2px solid var(--w-color-focused);outline-offset:var(--w-outline-offset, 1px);}.focusable:not(:focus-visible){outline:none;}
.group:focus .group-focus\\\\:focusable,.group:focus-visible .group-focus\\\\:focusable{outline:2px solid var(--w-color-focused);outline-offset:var(--w-outline-offset, 1px);}.group:not(:focus-visible) .group-focus\\\\:focusable{outline:none;}
.peer:focus~.peer-focus\\\\:focusable,.peer:focus-visible~.peer-focus\\\\:focusable{outline:2px solid var(--w-color-focused);outline-offset:var(--w-outline-offset, 1px);}.peer:not(:focus-visible)~.peer-focus\\\\:focusable{outline:none;}
.focusable-inset{--w-outline-offset:-3px;}"
`);
});
6 changes: 6 additions & 0 deletions test/variants.js
Expand Up @@ -24,5 +24,11 @@ describe('variants', () => {
const { css } = await uno.generate(classes);
expect(css).toMatchSnapshot();
});
test('custom', async ({ uno }) => {
const classes = ['last-child:mb-0'];
const { css } = await uno.generate(classes);
expect(css).toMatchSnapshot();
});

// space/divide variant is already tested by the space/divide classes
});

0 comments on commit 40e75cf

Please sign in to comment.