├── .editorconfig
├── .github
├── CODEOWNERS
├── FUNDING.yml
├── ci.yml
├── issue_template.md
└── pull_request_template.md
├── .gitignore
├── .husky
├── commit-msg
└── pre-commit
├── .npmignore
├── .prettierrc
├── BACKLOG.md
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── __tests__
└── index.spec.ts
├── babel.config.js
├── commitlint.config.js
├── demo
├── .gitignore
├── index.html
├── package.json
├── src
│ ├── App.vue
│ ├── components
│ │ └── Button.vue
│ ├── env.d.ts
│ └── main.ts
├── tsconfig.json
└── vite.config.ts
├── jest.config.ts
├── package.json
├── rollup.config.ts
├── src
├── demo.ts
├── index.ts
└── types.ts
├── tsconfig.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 | max_line_length=120
12 |
13 | [*.md]
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # More details are here: https://help.github.com/articles/about-codeowners/
2 |
3 | * @lukasborawski
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [lukasborawski]
4 | buymeacoffee: [lukasborawski]
5 |
--------------------------------------------------------------------------------
/.github/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - develop
7 | pull_request:
8 | branches:
9 | - main
10 | - master
11 | - develop
12 |
13 | jobs:
14 | CI:
15 | runs-on: ${{ matrix.os }}
16 |
17 | strategy:
18 | matrix:
19 | os: [ubuntu-latest]
20 | node: [14]
21 |
22 | steps:
23 | - name: Checkout 🛎
24 | uses: actions/checkout@master
25 |
26 | - name: Setup node env 🏗
27 | uses: actions/setup-node@v2.1.5
28 | with:
29 | node-version: ${{ matrix.node }}
30 | check-latest: true
31 |
32 | - name: Cache node_modules 📦
33 | uses: actions/cache@v2
34 | with:
35 | path: ~/.npm
36 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
37 | restore-keys: |
38 | ${{ runner.os }}-node-
39 |
40 | - name: Install dependencies 👨🏻💻
41 | run: npm ci
42 |
43 | - name: Run tests 🧪
44 | run: npm run test --passWithNoTests
45 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 | **Description**:
2 | ---
3 | Some issue description.
4 |
5 | **Tasks**:
6 | ---
7 | - [ ] task name
8 |
9 | **Research**:
10 | ---
11 | None
12 |
13 | **Screenshots**:
14 | ---
15 | None
16 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | Closing: #[issue number]
2 |
3 | **Tasks**:
4 | ---
5 | * [ ] issue connected
6 | * [ ] summary
7 | * [ ] self review
8 |
9 | **Summary**:
10 | ---
11 | * *write here some summary points*
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Node template
3 | # Logs
4 | /logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | lerna-debug.log*
9 | yarn-error.log*
10 |
11 | # Runtime data
12 | pids
13 | *.pid
14 | *.seed
15 | *.pid.lock
16 |
17 | # Directory for instrumented libs generated by jscoverage/JSCover
18 | lib-cov
19 |
20 | # Coverage directory used by tools like istanbul
21 | coverage
22 |
23 | # nyc test coverage
24 | .nyc_output
25 |
26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
27 | .grunt
28 |
29 | # Bower dependency directory (https://bower.io/)
30 | bower_components
31 |
32 | # node-waf configuration
33 | .lock-wscript
34 |
35 | # Compiled binary addons (https://nodejs.org/api/addons.html)
36 | build/Release
37 |
38 | # Dependency directories
39 | node_modules/
40 | jspm_packages/
41 |
42 | # TypeScript v1 declaration files
43 | typings/
44 |
45 | # Optional npm cache directory
46 | .npm
47 |
48 | # Optional eslint cache
49 | .eslintcache
50 |
51 | # Optional REPL history
52 | .node_repl_history
53 |
54 | # Output of 'npm pack'
55 | *.tgz
56 |
57 | # Yarn Integrity file
58 | .yarn-integrity
59 |
60 | # dotenv environment variables file
61 | .env
62 |
63 | # parcel-bundler cache (https://parceljs.org/)
64 | .cache
65 |
66 | # next.js build output
67 | .next
68 |
69 | # nuxt.js build output
70 | .nuxt
71 |
72 | # Nuxt generate
73 | dist
74 |
75 | # vuepress build output
76 | .vuepress/dist
77 |
78 | # Serverless directories
79 | .serverless
80 |
81 | # IDE / Editor
82 | .idea
83 |
84 | # Service worker
85 | sw.*
86 |
87 | # macOS
88 | .DS_Store
89 |
90 | # Vim swap files
91 | *.swp
92 |
93 | # Server
94 | .reify-cache
95 | **/**/.reify-cache
96 |
97 | lib
98 | .husky/_
99 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx --no-install commitlint --edit "$1" --config ./commitlint.config.js
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx npm run test
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | src
4 | demo
5 | __tests__
6 | .husky
7 | .github
8 |
9 | .editorconfig
10 | .idea
11 | .prettierrc
12 | babel.config.js
13 | commitlint.config.js
14 | jest.config.ts
15 | rollup.config.ts
16 | tsconfig.json
17 | yarn.lock
18 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "semi": false,
4 | "singleQuote": true,
5 | "arrowParens": "always",
6 | "tabWidth": 2,
7 | "trailingComma": "all"
8 | }
9 |
--------------------------------------------------------------------------------
/BACKLOG.md:
--------------------------------------------------------------------------------
1 | ### Missing features, things to do:
2 |
3 | - [ ] get variants of top of array `[base, primary]`
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### 0.1.2
2 |
3 | - handling variants as an array of strings [#1]
4 | - add demo more examples
5 | - output terser
6 | - docs update
7 |
8 | ### 0.1.1
9 |
10 | - removed unnecessary dependencies
11 | - add the demo missing types
12 | - docs update
13 |
14 | ### 0.1.0
15 |
16 | - initial package state
17 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | ISC License (ISC)
2 | Copyright 2021 Lukas Borawski
3 |
4 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
5 |
6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Vue Use Variant
2 |
3 |
4 |
5 |
6 |
7 |
8 | Simple composable for **Vue.js*** to handle long and ugly CSS class chaining.
9 |
10 | Read the story behind this package [here](https://itnext.io/tailwind-class-madness-never-again-75ec6ebfd3a0).
11 |
12 | **you can use it with any other framework as well*
13 |
14 | ### Install
15 |
16 | ---
17 |
18 | Install the package:
19 |
20 | ```bash
21 | $ npm i vue-use-variant --save
22 | # or
23 | $ yarn add vue-use-variant
24 | ```
25 |
26 | ### Motivation
27 |
28 | ---
29 |
30 | We all know that systems like [Tailwind](https://tailwindcss.com) are awesome, but we also know that defining styles by using utility classes can be hard ... Let's say you are using Tailwind and you want to style some button with provided classes. Probably the definition will look something like this:
31 |
32 | ```vue
33 |
48 | ```
49 |
50 | ... and this is just a button. Imagine whole markup for even tiny component.
51 | Readability of it will be - easy talking - not so great. So what **problems** we're facing here:
52 |
53 | - very long class definition
54 | - poor readability
55 | - lack of scalability
56 | - hard to maintain
57 |
58 | ### Usage
59 |
60 | ---
61 |
62 | To resolve these problems you can try `useVariant` composable.
63 |
64 | First define some variants. You can crate regular JSON object for it or use Vue `Ref`.
65 |
66 | ```javascript
67 | import { ref } from 'vue'
68 |
69 | export const buttonVariants = {
70 | button: 'font-bold rounded border-0 bg-blue hover:opacity-80',
71 | buttonPrimary: 'p-4 text-lg',
72 | buttonSecondary: 'p-2 text-md',
73 | }
74 |
75 | // or use with Vue Ref (Composition API)
76 | export const buttonVariantsRef = ref(buttonVariants)
77 | ```
78 |
79 | Now let's see how we can use it with some real component example.
80 |
81 | ```vue
82 |
83 |
84 |
85 |
86 |
108 | ```
109 |
110 | As a result your button will get this set of classes:
111 |
112 | ```vue
113 | font-bold rounded border-0 bg-blue hover:opacity-80 p-4 text-lg
114 | ```
115 |
116 | ---
117 |
118 | You can also use it with **props**.
119 |
120 | ```vue
121 |
145 | ```
146 |
147 | Use as an Array.
148 |
149 | ```vue
150 |
164 | ```
165 |
166 | Use straight without variant definitions.
167 |
168 | ```vue
169 |
181 | ```
182 |
183 | Finally, you can define your variants as composable argument.
184 |
185 | ```vue
186 |
200 | ```
201 |
202 | Of course, you can **combine and mix** variants and use them as a global variations for
203 | your base, global and reusable components. It's super easy and convenient. You can
204 | of course use it with any **other UI System** like for example Boostrap or Vuetify.
205 | And maybe it was built for vue you can use it for any o**ther frameworks** like React.
206 |
207 | ---
208 |
209 | ### Demo
210 |
211 | Want to check or test it in action? Check out the simple app in the `demo` folder.
212 |
213 | ---
214 |
215 | **API Reference**: Check out the [types](src/types.ts) for API definitions.
216 |
217 | **Contribution**: Please add Pull Request to introduce some changes or fixes.
218 |
219 | **Support**: Want to support? [Buy me a coffee](https://www.buymeacoffee.com/lukas.borawski).
220 |
221 |
222 |
223 |
--------------------------------------------------------------------------------
/__tests__/index.spec.ts:
--------------------------------------------------------------------------------
1 | import { useVariant } from '~/lib/index.es'
2 |
3 | describe('vue-use-variant', () => {
4 | const variantsDefinitions = {
5 | button: 'button-class',
6 | buttonPrimary: 'button-class-primary',
7 | }
8 |
9 | it('Renders class string with regular configuration', () => {
10 | const { defineVariant } = useVariant()
11 | const variants = defineVariant({ button: true, buttonPrimary: true }, variantsDefinitions)
12 | // @ts-ignore
13 | expect(variants).toBe('button-class button-class-primary')
14 | })
15 | it('Renders class string with composable passed definitions', () => {
16 | const { defineVariant } = useVariant(variantsDefinitions)
17 | const variants = defineVariant({ button: true, buttonPrimary: true })
18 | // @ts-ignore
19 | expect(variants).toBe('button-class button-class-primary')
20 | })
21 | it('Renders class string with custom value', () => {
22 | const { defineVariant } = useVariant()
23 | const variants = defineVariant({ button: 'button-class-primary' })
24 | // @ts-ignore
25 | expect(variants).toBe('button-class-primary')
26 | })
27 | it('Renders class string with Vue Ref object', () => {
28 | const { defineVariant } = useVariant()
29 | const variants = defineVariant(
30 | {
31 | value: {
32 | button: true,
33 | buttonPrimary: true,
34 | },
35 | },
36 | variantsDefinitions,
37 | )
38 | // @ts-ignore
39 | expect(variants).toBe('button-class button-class-primary')
40 | })
41 | it('Renders class string with Vue props', () => {
42 | const { defineVariant } = useVariant()
43 | const props = {
44 | button: true,
45 | buttonProp: 'button-class-primary',
46 | }
47 | const variants = defineVariant({ ...props }, variantsDefinitions)
48 | // @ts-ignore
49 | expect(variants).toBe('button-class button-class-primary')
50 | })
51 | it('Renders class string with an array', () => {
52 | const { defineVariant } = useVariant()
53 | const variants = defineVariant(['button', 'buttonPrimary'], variantsDefinitions)
54 | // @ts-ignore
55 | expect(variants).toBe('button-class button-class-primary')
56 | })
57 | it('Fails when using wrong variants data', () => {
58 | // @ts-ignore
59 | global.console = { warn: jest.fn() }
60 | const { defineVariant } = useVariant()
61 | defineVariant({ button: { test: true }, buttonPrimary: true }, variantsDefinitions)
62 | // @ts-ignore
63 | expect(console.warn).toBeCalled()
64 | })
65 | })
66 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | { targets: { node: 'current' } }
6 | ],
7 | '@babel/preset-typescript',
8 | ],
9 | }
10 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional'],
3 | }
4 |
--------------------------------------------------------------------------------
/demo/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | yarn.lock
7 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |