diff --git a/packages/next-yak/README.md b/packages/next-yak/README.md index 5d4c7187..69a2bcb1 100644 --- a/packages/next-yak/README.md +++ b/packages/next-yak/README.md @@ -16,8 +16,7 @@ - **Standard CSS Syntax**: Write styles in familiar, easy-to-use CSS - **Integrates with Atomic CSS**: Easily combines with atomic CSS frameworks like Tailwind CSS for more design options - - +[Preview (Video)](https://github.com/jantimon/next-yak/assets/4113649/5383f60c-3bab-4aba-906c-d66070b6116c) ## Installation @@ -90,7 +89,7 @@ const ToggleButton = styled.button` `; ``` - +[Dynamic Styles (Video)](https://github.com/jantimon/next-yak/assets/4113649/c5f52846-33e4-4058-9c78-efd98197d75f) ### Dynamic Properties @@ -121,7 +120,8 @@ const ExampleComponent = () => { }; ``` - +[Dynamic Props (video)](https://github.com/jantimon/next-yak/assets/4113649/2fa78f82-382c-465f-b294-2504739ea168) + ### Targeting Components @@ -158,7 +158,7 @@ const ExampleComponent = () => { ## Nesting `next-yak` supports nesting out of the box. -Next.js 13 supports nesting only with the `postcss-nested` plugin. +[For now](https://github.com/css-modules/postcss-modules-local-by-default/pull/64) Next.js 13 supports nesting only with the `postcss-nested` plugin. Therefore you have to create a `postcss.config.js` file in your project root: ```js @@ -170,6 +170,8 @@ module.exports = { }; ``` +[Nesting Example (video)](https://github.com/jantimon/next-yak/assets/4113649/33eeeb13-b0cf-499f-a1d3-ba6f51cf4308) + ## Motivation Most of the existing CSS-in-JS libraries are either slow or have a complex api. This project tries to find a middle ground between speed and api complexity. diff --git a/packages/next-yak/loaders/__tests__/tsloader.test.ts b/packages/next-yak/loaders/__tests__/tsloader.test.ts index 1ac0bc7f..4bfc337a 100644 --- a/packages/next-yak/loaders/__tests__/tsloader.test.ts +++ b/packages/next-yak/loaders/__tests__/tsloader.test.ts @@ -356,8 +356,7 @@ const Wrapper = styled.div\` `); }); - // TODO: this test was temporarily disabled because it was failing when inline css literals were introduced - it.skip("should show error when mixin is used in nested selector", async () => { + it("should show error when mixin is used in nested selector", async () => { await expect(() => tsloader.call( loaderContext, @@ -378,9 +377,36 @@ const Icon = styled.div\` ` ) ).rejects.toThrowErrorMatchingInlineSnapshot(` - "/some/special/path/page.tsx: line 11: Expressions are not allowed inside nested selectors: - \\"bold\\" inside \\"@media (min-width: 640px) { .bar {\\" - found: \${bold}" + "/some/special/path/page.tsx: line 11: Mixins are not allowed inside nested selectors + found: \${bold} + Use an inline css literal instead or move the selector into the mixin" + `); + }); + + it("should show error when a mixin function is used in nested selector", async () => { + await expect(() => + tsloader.call( + loaderContext, + ` +import { styled, css } from "next-yak"; + +const bold = () => css\` + font-weight: bold; +\` + +const Icon = styled.div\` + @media (min-width: 640px) { + .bar { + \${bold()} + } + } +\` +` + ) + ).rejects.toThrowErrorMatchingInlineSnapshot(` + "/some/special/path/page.tsx: line 11: Mixins are not allowed inside nested selectors + found: \${bold()} + Use an inline css literal instead or move the selector into the mixin" `); }); @@ -406,9 +432,9 @@ const Icon = styled.div\` ` ) ).rejects.toThrowErrorMatchingInlineSnapshot(` - "/some/special/path/page.tsx: line 11: Expressions are not allowed inside nested selectors: - Expression inside \\"@media (min-width: 640px) { .bar {\\" - found: \${() => css\`\${bold}\`}" + "/some/special/path/page.tsx: line 11: Mixins are not allowed inside nested selectors + found: \${bold} + Use an inline css literal instead or move the selector into the mixin" `); }); it("should show error when a dynamic selector is used", async () => { diff --git a/packages/next-yak/loaders/babel-yak-plugin.cjs b/packages/next-yak/loaders/babel-yak-plugin.cjs index 07839673..d871a9be 100644 --- a/packages/next-yak/loaders/babel-yak-plugin.cjs +++ b/packages/next-yak/loaders/babel-yak-plugin.cjs @@ -340,13 +340,17 @@ module.exports = function (babel, options) { wasInsideCssValue = false; if (expression) { if (quasiTypes[i].currentNestingScopes.length > 0) { - // TODO: inside a nested scope a foreign css literal must not be used - // as we can not forward the scope. - // Therefore the following code must throw an error: - // import { mixin } from "./some-file"; - // const Button = styled.button` - // &:focus { ${mixin} } - // ` + // inside a nested scope a foreign css literal must not be used + // as we can not forward the scope + const isReferenceToMixin = t.isIdentifier(expression) || t.isCallExpression(expression); + if (isReferenceToMixin) { + throw new InvalidPositionError( + `Mixins are not allowed inside nested selectors`, + expression, + this.file, + "Use an inline css literal instead or move the selector into the mixin" + ); + } } newArguments.add(expression); } @@ -393,8 +397,9 @@ class InvalidPositionError extends Error { * @param {string} message * @param {import("@babel/types").Expression} expression * @param {import("@babel/core").BabelFile} file + * @param {string} [recommendedFix] */ - constructor(message, expression, file) { + constructor(message, expression, file, recommendedFix) { let errorText = message; const line = expression.loc?.start.line ?? -1; if (line !== -1) { @@ -406,6 +411,9 @@ class InvalidPositionError extends Error { expression.end )}}`; } + if (recommendedFix) { + errorText += `\n${recommendedFix}`; + } super(errorText); } } diff --git a/packages/next-yak/package.json b/packages/next-yak/package.json index df5ef643..ad34d33a 100644 --- a/packages/next-yak/package.json +++ b/packages/next-yak/package.json @@ -1,6 +1,6 @@ { "name": "next-yak", - "version": "0.0.21", + "version": "0.0.22", "type": "module", "types": "./dist/", "exports": {