tailwindlabs / tailwindcss

A utility-first CSS framework for rapid UI development.
https://tailwindcss.com/
MIT License
82.7k stars 4.19k forks source link

Import does not work like documented #45

Closed HellPat closed 6 years ago

HellPat commented 6 years ago

I'm trying to include a component. In the comments of the "bootstrap file" @import is used. This is not a funcionality provided by tailwind or is it?

I expect that I have to add postcss-import to my workflow. Am I wrong or are the docs wrong here?

If i import a file like dokumented i get an error.

/**
 * This injects Tailwind's base styles, which is a combination of
 * Normalize.css and some additional base styles.
 *
 * You can see the styles here:
 * https://github.com/tailwindcss/tailwindcss/blob/master/css/preflight.css
 */
@tailwind preflight;

/**
 * Here you would import any custom component classes; stuff that you'd
 * want loaded *before* the utilities so that the utilities can still
 * override them.
 * 
 * @import "my-components/foo";
 * @import "my-components/bar";
 */

@import "my-components/bar"; /*I added this one*/

/**
 * This injects all of Tailwind's utility classes, generated based on your
 * config file.
 */
@tailwind utilities;

And here the error:

@import must precede all other statements (besides @charset)

What am I doing wrong?

reinink commented 6 years ago

If you're using Tailwind with plain old CSS (which you can totally do), then you'll need to use a PostCSS library that supports inline imports. I use postcss-import. You may also want to look at using cssnext, which I believe includes this by default.

Let me know if that doesn't solve your issues!

SmashBrando commented 6 years ago

@reinink I'm also trying to get this to work using the postcss-import plugin and am getting the same error. @import must precede all other statements (besides @charset)

Would you mind sharing a sample gulp config?

Here is mine.

var gulp = require('gulp');
gulp.task('css', function () {
  var postcss = require("gulp-postcss");
  var atimport = require('postcss-import');
  var tailwindcss = require("tailwindcss");
  return gulp.src('styles/style.css')
    .pipe(postcss([
      atimport(),
      tailwindcss('./styles/config.js'),
      require('autoprefixer'),
    ]))
    .pipe(gulp.dest(''));
});

Edit: after a bit more digging, I can get this to work by creating a style.css entry point that imports my base tailwinds css and then any other files.

HellPat commented 6 years ago

@reinink the trick mentioned by @SmashBrando works. I already use postcss-import, but this plugin produced this warning.

Now I import the @tailwind preflight; from an other file. My main.css looks like that:

@import "_reset";
@import "_breadcrumb";
@import "_utilities";

and i moved the @tailwind … calls to _reset/_utilities.

This is not optimal, maybe we can document this behaviour on https://tailwindcss.com/docs/installation in a extra using with postcss-import seciton or smth. like that?

Or maybe you a better way and want to share your config?

dotnetCarpenter commented 6 years ago

I'm using nuxt and have set up tailwind according to the nuxt example. I get the same error message which makes me think that the documentation is wrong for tailwind webpack.


 warning  in ./assets/css/tailwind.css

(Emitted value instead of an instance of Error) postcss-import: /Users/nappdev/projects/webviewer2/assets/css/tailwind.css:25:4:
 @import must precede all other statements (besides @charset)

 @ ./assets/css/tailwind.css 4:14-147 13:3-17:5 14:22-155
 @ ./.nuxt/App.js
 @ ./.nuxt/index.js
 @ ./.nuxt/client.js
 @ multi webpack-hot-middleware/client?name=client&reload=true&timeout=30000&path=/__webpack_hmr ./.nuxt/client.js

I've set up postcss.config.jsas:

module.exports = {
  plugins: [
    require('tailwindcss')('./tailwind.js'),
    require('postcss-import'),
    require('postcss-custom-properties'),
    require('postcss-color-function'),
    require('autoprefixer')
  ]
}

My tailwind.css is:

 @tailwind preflight;

 @import "variables.css";
 @import "icons.css";

 @tailwind utilities;
dotnetCarpenter commented 6 years ago

@psren Your work-around is the only way I can get it to work. I tried to require postcss-import before tailwindcss but that did nothing.

I've changed my tailwind.css to:

@import "preflight.css";
/**
  * Here you would add any of your custom component classes; stuff that you'd
  * want loaded *before* the utilities so that the utilities could still
  * override them.
  *
  * Example:
  *
  * .btn { ... }
  * .form-input { ... }
  *
  * Or if using a preprocessor:
  *
  * @import "components/buttons";
  * @import "components/forms";
  */
@import "variables.css";
@import "icons.css";

@import "utilities.css";

/**
  * Here you would add any custom utilities you need that don't come out of the
  * box with Tailwind.
  *
  * Example :
  *
  * .bg-pattern-graph-paper { ... }
  * .skew-45 { ... }
  *
  * Or if using a preprocessor..
  *
  * @import "utilities/backgrond-patterns";
  * @import "utilities/skew-transforms";
  */

preflight.css and utilities.css contains the 2 tailwind calls

adamwathan commented 6 years ago

@dotnetCarpenter You need to run postcss-import before Tailwind and make sure any @tailwind calls are moved out to their own files to satisfy that error you pasted:

@import must precede all other statements (besides @charset)

The problem is @tailwind is coming before an @import which is invalid according to the CSS spec, and postcss-import is strict about that. You'd get the same error even if you had a file like this:

.foo {
  color: blue;
}

@import "icons.css";

So it's not really a Tailwind issue specifically, just that when using postcss-import you can't have any additional code before any @import statements.

So the solution like you noticed is to make your main CSS file only import statements, so you're free to order them as you like. This means wrapping all other code (including @tailwind statements) in separate files.

dotnetCarpenter commented 6 years ago

Yes you're right. Which is also what I have done. The issue is that the example does not and I bet that most nuxt users, like me, will copy the example before they google and find the work-around. I've updated the example to reflect the work-around...

Sorry posted in wrong issue.. Moved to https://github.com/nuxt/nuxt.js/issues/2094

dotnetCarpenter commented 6 years ago

Hmm even @reinink says it inlines imports... https://github.com/tailwindcss/tailwindcss/issues/45#issuecomment-341281707

adamwathan commented 6 years ago

It does inline imports, it's just strict about the fact that imports must come first in the file. You copy and pasted an error you saw yourself explaining that:

 warning  in ./assets/css/tailwind.css

(Emitted value instead of an instance of Error) postcss-import: /Users/nappdev/projects/webviewer2/assets/css/tailwind.css:25:4:
 @import must precede all other statements (besides @charset)

 @ ./assets/css/tailwind.css 4:14-147 13:3-17:5 14:22-155
 @ ./.nuxt/App.js
 @ ./.nuxt/index.js
 @ ./.nuxt/client.js
 @ multi webpack-hot-middleware/client?name=client&reload=true&timeout=30000&path=/__webpack_hmr ./.nuxt/client.js

That stack trace is postcss-import saying "imports must come before any other code in a file".

Since @tailwind calls are not imports, it errors. As we've talked about in this thread, the solution is to wrap all of your code in separate files. Again, it's not a Tailwind-specific issue; that's how that plugin works.

It inlines the imports perfectly as long as your imports come before other code, like the error message explains, and like you noticed after extracting the Tailwind calls to separate files.

dotnetCarpenter commented 6 years ago

Thanks for clarifying. I think the following is very confusing:

/* Or if using a preprocessor:
*
* @import "components/buttons";
* @import "components/forms";
*/

Since PostCSS is a transpiler, which makes it a preprocessor in my book, not writing how to use tailwind with PostCSS import transformations is very confusing. Can we agree to a different wording?

HellPat commented 6 years ago

Since tailwind calls are not imports, it errors. As we've talked about in this thread, the solution is to wrap all of your code in separate files. Again, it's not a Tailwind-specific issue; that's how that plugin works.

thats all true.

Mmmh, maybe we could add some example configurations to https://tailwindcss.com/docs/installation#3-use-tailwind-in-your-css and not only the one example.

This should not be a problem in maintenance because this one file most likely won't change at all.

adamwathan commented 6 years ago

@psren Totally, we actually plan to add a separate page for each that documents any of the preprocessor-specific gotchas, like how you can't nest @screen with Less, how you have to disable processCssUrls using Sass with Laravel Mix, or how you have to wrap Tailwind features with @css {} in Stylus.

Maybe just linking to some quick gists for each one from the docs would be a good interim solution though.

harrygreen commented 6 years ago

Some people reading this far might find this summary useful. I solved this by switching the following lines in my main CSS file from:

@tailwind preflight;
@tailwind components;
@tailwind utilities;

to:

@import "tailwindcss/preflight";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

and ensuring postcss-import came before tailwind in my postcss config:

require('postcss-import'),
require('tailwindcss')('./src/css/tailwind.js'),

Thanks to the points made by @dotnetCarpenter and @adamwathan.

rhyswat commented 6 years ago

tailwind + webpack + postcss-import

Whether I missed this or not, one thing in addition to @harrygreen's suggestion is this: @apply and @import in the same root css file will break things.

My webpack.config.js loads styles.css

const ExtractTextPlugin = require('extract-text-webpack-plugin')

const path = require('path')

module.exports = {
  entry: './index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'styles.css',
  },
  module: {
    rules: [{
      test: /\.css$/,
      exclude: /node_modules/,
      use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: [{
            loader: 'css-loader',
            options: {
              importLoaders: 1
            }
          },
          'postcss-loader'
        ]
      })
    }]
  },
  plugins: [
    new ExtractTextPlugin('styles.css')
  ]
}

This top-level styles.css only at-imports things:

/** Tailwind */
@import "tailwindcss/preflight";
@import "tailwindcss/components";

/** My components. */
@import "./alerts.css";

/* This would fail ---
.something {
   @apply .m-2
}
*/

/** Tailwind again */
@import "tailwindcss/utilities";

My alerts.css component:

.alert {
    @apply .m-2 .px-4 .py-3 .rounded
}
/* etc */
adamwathan commented 6 years ago

@rhyswat You're misunderstanding the problem in your example — it's not that using @apply in the same file as @import breaks things, it's that you cannot have any non-import statements before any import statements.

In your example the reason adding the rule with @apply breaks things is because now you have a non-import statement preceding an import statement.

postcss-import will refuse to process this file for example:

.foo { color: blue }

@import "tailwindcss/utilities";

...because .foo { ... } comes before @import "tailwindcss/utilities" which is not allowed as per the spec:

The @import CSS at-rule is used to import style rules from other style sheets. These rules must precede all other types of rules, except @charset rules.

This will totally work:

/** Tailwind */
@import "tailwindcss/preflight";
@import "tailwindcss/components";

/** My components. */
@import "./alerts.css";

/** Tailwind again */
@import "tailwindcss/utilities";

.something {
   @apply .m-2
}

But you might not want your CSS in that order. In that case, the correct solution is for this file to only contain import statements and any non-import rules to other files that can be imported, so that you are following the spec while still having control over your CSS order.

So again it has nothing to do with @apply, postcss-import just doesn't allow you to have any CSS come before any import statements.

yacinelazaar commented 5 years ago

@adamwathan Can you share a sample vue-cli project where we can use the @apply directive in imported stylesheets? I wasted a day trying every suggested solution in here.

mateo2181 commented 5 years ago

I have the same problem, I am trying this in a Laravel project but I can't do it using scss.

CesarRotela commented 5 years ago

Some people reading this far might find this summary useful. I solved this by switching the following lines in my main CSS file from:

@tailwind preflight;
@tailwind components;
@tailwind utilities;

to:

@import "tailwindcss/preflight";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

and ensuring postcss-import came before tailwind in my postcss config:

require('postcss-import'),
require('tailwindcss')('./src/css/tailwind.js'),

Thanks to the points made by @dotnetCarpenter and @adamwathan.

Some people reading this far might find this summary useful. I solved this by switching the following lines in my main CSS file from:

@tailwind preflight;
@tailwind components;
@tailwind utilities;

to:

@import "tailwindcss/preflight";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

and ensuring postcss-import came before tailwind in my postcss config:

require('postcss-import'),
require('tailwindcss')('./src/css/tailwind.js'),

Thanks to the points made by @dotnetCarpenter and @adamwathan.

Thank you! thank you! you saved me many hours of work, I could not find a solution that worked for me! Now I just need to work with postcss-font-magician and it would be great!!

ewhicher commented 5 years ago

@adamwathan I think the docs miss this tiny piece from @harrygreen 's answer above:

...and ensuring postcss-import came before tailwind in my postcss config

I was pulling my hair out till I found this 🙂

hacknug commented 5 years ago

Already mentioned in this page:

Then add it as the very first plugin in your PostCSS configuration

burmashave commented 4 years ago

Already mentioned in this page:

Then add it as the very first plugin in your PostCSS configuration

True, but the examples in the installation document do not make this clear.

mikebronner commented 4 years ago

Keep in mind that some postcss plugins, like postcss-import, must come before tailwind to make it work.

hacknug commented 4 years ago

@burmashave that's because it only mentions how to install tailwindcss itself on that page. It doesn't make any assumptions on what other plugins you might or might not use.

From the postcss-import docs:

This plugin should probably be used as the first plugin of your list. This way, other plugins will work on the AST as if there were only a single file to process, and will probably work as you can expect.

Might be helpful to add a note about plugin order being important and affecting the generated CSS. Pretty sure a PR that mentions it would be welcome (without being too specific and refering to just one plugin).

burmashave commented 4 years ago

@burmashave that's because it only mentions how to install tailwindcss itself on that page. It doesn't make any assumptions on what other plugins you might or might not use.

@hacknug The section I linked to in the installation docs is entitled "Using Tailwind with PostCSS." So it clearly makes an assumption you might want to use Tailwind with ... PostCSS. The code snippets displayed there suggest that you only need to require 'tailwindcss' and 'autoprefixer', and make no mention of their order in the postcss.config.js file, unlike the "Build-time imports" section you linked to.