├── .gitignore ├── .npmignore ├── index.js ├── generator ├── templates │ ├── .eslintrc.js │ ├── postcss.config.js │ ├── src │ │ └── assets │ │ │ └── styles │ │ │ └── tailwind.postcss │ └── tailwind.config.js └── index.js ├── .editorconfig ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules* 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules* 2 | .* 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = () => {} 2 | -------------------------------------------------------------------------------- /generator/templates/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'indent': [ 'error', 2 ], 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = tab 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [**/templates/**] 12 | indent_style = space 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ky-is/vue-cli-plugin-tailwind", 3 | "version": "2.0.0", 4 | "description": "Write utility-first CSS with future standards in your Vue app using TailwindCSS.", 5 | "author": "Kyle Coburn", 6 | "license": "ISC", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/ky-is/vue-cli-plugin-tailwind.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/ky-is/vue-cli-plugin-tailwind/issues" 13 | }, 14 | "homepage": "https://github.com/ky-is/vue-cli-plugin-tailwind#readme", 15 | "devDependencies": { 16 | "@ky-is/eslint-config": "^1.7.0", 17 | "eslint": "^5.16.0" 18 | }, 19 | "eslintConfig": { 20 | "extends": "@ky-is" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /generator/templates/postcss.config.js: -------------------------------------------------------------------------------- 1 | const IN_PRODUCTION = process.env.NODE_ENV === 'production' 2 | 3 | module.exports = { 4 | plugins: [ 5 | require('postcss-preset-env')({ stage: 0 }), 6 | require('tailwindcss')(), 7 | IN_PRODUCTION && require('@fullhuman/postcss-purgecss')({ 8 | content: [ `./public/**/*.html`, `./src/**/*.vue` ], 9 | defaultExtractor (content) { 10 | const contentWithoutStyleBlocks = content.replace(//gi, '') 11 | return contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || [] 12 | }, 13 | whitelist: [], 14 | whitelistPatterns: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/ ], 15 | }), 16 | require('autoprefixer')(), 17 | ], 18 | } 19 | -------------------------------------------------------------------------------- /generator/templates/src/assets/styles/tailwind.postcss: -------------------------------------------------------------------------------- 1 | @import '~tailwindcss/base.css'; 2 | 3 | /** 4 | * This injects any component classes registered by plugins. 5 | */ 6 | @import '~tailwindcss/components.css'; 7 | 8 | /** 9 | * Here you would add any of your custom component classes; stuff that you'd 10 | * want loaded *before* the utilities so that the utilities could still 11 | * override them. 12 | * 13 | * Example: 14 | * @import 'components/buttons'; 15 | */ 16 | 17 | /** 18 | * This injects all of Tailwind's utility classes, generated based on your 19 | * config file. 20 | */ 21 | @import '~tailwindcss/utilities.css'; 22 | 23 | /** 24 | * Here you would add any custom utilities you need that don't come out of the 25 | * box with Tailwind. 26 | * 27 | * Example: 28 | * @import 'utilities/background-patterns'; 29 | */ 30 | -------------------------------------------------------------------------------- /generator/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (api, options) => { 2 | function findFileInNamed (files, name) { 3 | const searchName = `/${name.toLowerCase()}` 4 | return Object.keys(files).find(fileName => fileName.toLowerCase().includes(searchName)) 5 | } 6 | 7 | api.extendPackage({ 8 | postcss: undefined, 9 | 10 | devDependencies: { 11 | "@fullhuman/postcss-purgecss": "^1.2.0", 12 | "postcss-preset-env": "^6.6.0", 13 | "tailwindcss": "^1.0.1", 14 | }, 15 | }) 16 | 17 | api.render('./templates', options) 18 | 19 | api.postProcessFiles(files => { 20 | const importRelativePath = 'assets/styles/tailwind' 21 | const searchName = 'App.vue' 22 | const appFileName = findFileInNamed(files, searchName) 23 | const importExtension = 'postcss' 24 | if (!appFileName) { 25 | return api.exitLog(`${searchName} file not found. Please import '${importRelativePath}' manually.`, 'error') 26 | } 27 | const appFileString = files[appFileName] 28 | if (appFileString.includes(importRelativePath)) { 29 | return 30 | } 31 | const mainFileString = files[findFileInNamed(files, 'main.')] 32 | if (mainFileString && mainFileString.includes(importRelativePath)) { 33 | return 34 | } 35 | const importStatement = `\n@import '${importRelativePath}.${importExtension}';\n` 36 | const lines = appFileString.split(/\r?\n/g) 37 | const styleIndex = lines.findIndex(line => line.startsWith('${importStatement}\n\n` 42 | } 43 | files[appFileName] = lines.join('\n') 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /generator/templates/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | extend: { 4 | inset: { 5 | 'full': '100%', 6 | }, 7 | opacity: { 8 | '10': '0.1', 9 | '90': '0.9', 10 | }, 11 | }, 12 | }, 13 | variants: { 14 | appearance: ['responsive'], 15 | backgroundAttachment: ['responsive'], 16 | backgroundColor: ['responsive', 'hover', 'focus'], 17 | backgroundPosition: ['responsive'], 18 | backgroundRepeat: ['responsive'], 19 | backgroundSize: ['responsive'], 20 | borderCollapse: [], 21 | borderColor: ['responsive', 'hover', 'focus'], 22 | borderRadius: ['responsive'], 23 | borderStyle: ['responsive'], 24 | borderWidth: ['responsive'], 25 | cursor: ['responsive'], 26 | display: ['responsive', 'group-hover'], 27 | flexDirection: ['responsive'], 28 | flexWrap: ['responsive'], 29 | alignItems: ['responsive'], 30 | alignSelf: ['responsive'], 31 | justifyContent: ['responsive'], 32 | alignContent: ['responsive'], 33 | flex: ['responsive'], 34 | flexGrow: ['responsive'], 35 | flexShrink: ['responsive'], 36 | float: ['responsive'], 37 | fontFamily: ['responsive'], 38 | fontWeight: ['responsive', 'hover', 'focus'], 39 | height: ['responsive'], 40 | lineHeight: ['responsive'], 41 | listStylePosition: ['responsive'], 42 | listStyleType: ['responsive'], 43 | margin: ['responsive'], 44 | maxHeight: ['responsive'], 45 | maxWidth: ['responsive'], 46 | minHeight: ['responsive'], 47 | minWidth: ['responsive'], 48 | negativeMargin: ['responsive'], 49 | objectFit: ['responsive'], 50 | objectPosition: ['responsive'], 51 | opacity: ['responsive'], 52 | outline: ['focus'], 53 | overflow: ['responsive'], 54 | padding: ['responsive'], 55 | pointerEvents: ['responsive'], 56 | position: ['responsive'], 57 | inset: ['responsive'], 58 | resize: ['responsive'], 59 | boxShadow: ['responsive', 'hover', 'focus'], 60 | fill: [], 61 | stroke: [], 62 | tableLayout: ['responsive'], 63 | textAlign: ['responsive'], 64 | textColor: ['responsive', 'hover', 'focus'], 65 | fontSize: ['responsive'], 66 | fontStyle: ['responsive'], 67 | textTransform: ['responsive'], 68 | textDecoration: ['responsive', 'hover', 'focus'], 69 | fontSmoothing: ['responsive'], 70 | letterSpacing: ['responsive'], 71 | userSelect: ['responsive'], 72 | verticalAlign: ['responsive'], 73 | visibility: ['responsive', 'hover', 'group-hover'], 74 | whitespace: ['responsive'], 75 | wordBreak: ['responsive'], 76 | width: ['responsive'], 77 | zIndex: ['responsive'], 78 | }, 79 | corePlugins: { 80 | container: false, 81 | }, 82 | plugins: [], 83 | } 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-cli-plugin-tailwind 2 | 3 | [Tailwind CSS](https://tailwindcss.com/docs/what-is-tailwind)'s utility classes are minned by [Purgecss](https://www.purgecss.com), saving hundreds of kBs in production builds. [postcss-preset-env](https://preset-env.cssdb.org/features) polyfills modern CSS standards based on your `browserslist` configuration. 4 | 5 | 6 | ## Install 7 | 8 | ### TailwindCSS v1.0 9 | 10 | ```console 11 | vue add @ky-is/tailwind 12 | ``` 13 | 14 | When the plugin is updated, you can upgrade your configuration with: 15 | ```console 16 | vue invoke @ky-is/tailwind 17 | ``` 18 | 19 | ### TailwindCSS v0.x 20 | 21 | See the [`tailwind-0.x` branch](https://github.com/ky-is/vue-cli-plugin-tailwind/tree/tailwind-0.x). 22 | 23 | 24 | ## Usage 25 | 26 | Use inline classes, or `@apply`. For example, in `src/components/HelloWorld.vue` of the auto-generated cli app: 27 | ```html 28 | 33 | ``` 34 | 35 | Applies scoped, browser-prefixed CSS, while PurgeCSS strips all other unused classes, including the thousands generated by Tailwind. 36 | 37 | 38 | ## Configuration 39 | 40 | ### `postcss.config.js` Plugins 41 | 42 | - `postcss-preset-env`: Defaults to stage 2, as these draft proposals are considered reasonably stable. If you want to enable handy experimental features like nested classes (`a { &:hover: {...} }`), change to `stage: 0`. You can safely delete this plugin from the list if you only write old CSS or use another preprocessor. 43 | 44 | - `@fullhuman/postcss-purgecss`: Purgecss removes all CSS classes that it can't find reference to. By default, all Vue and style files in the `src` folder are included. Adjust `content` array if you have CSS classes in other files. Add class names to the `whitelist` array you want to manually prevent PurgeCSS from removing if it thinks they're unused. 45 | 46 | ### Whitelisting 47 | 48 | Any CSS class that isn't used inside your `.html` files in `public/`, or by your `.vue` components (outside of the `