├── .github
└── dependabot.yml
├── .gitignore
├── .prettierrc.js
├── .travis.yml
├── .yarnrc.yml
├── README.md
├── demo
├── dist
│ └── styles.css
├── package.json
├── postcss.config.js
├── src
│ └── styles.css
└── tailwind.config.js
├── index.html
├── package.json
├── tailwindcss-elevation
├── .eslintignore
├── .eslintrc.js
├── CHANGELOG.md
├── README.md
├── index.js
├── package.json
├── src
│ ├── box-shadow.js
│ ├── config.js
│ ├── regex.js
│ ├── utilities.js
│ └── validate-options.js
└── test
│ ├── box-shadow.test.js
│ ├── fixtures
│ ├── default-config.js
│ ├── example.html
│ ├── hex-color-config.js
│ ├── invalid-config.js
│ ├── opacity-config.js
│ ├── rgb-color-config.js
│ ├── styles.css
│ └── var-color-config.js
│ ├── tailwind-build.test.js
│ ├── utilities.test.js
│ └── validate-options.test.js
└── yarn.lock
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "npm"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 | target-branch: "develop"
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .yarn/
2 | node_modules/
3 | tmp/
4 |
5 | # Ignore test reports
6 | .nyc_output/
7 | coverage/
8 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | env:
2 | global:
3 | - CC_TEST_REPORTER_ID=2ddd0c30ab8dcec03310e7a49407cb7a3dc2dd051e4c7b8f0d2b2818a8242b45
4 | language: node_js
5 | node_js:
6 | - "lts/*"
7 | before_script:
8 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
9 | - chmod +x ./cc-test-reporter
10 | - ./cc-test-reporter before-build
11 | script: yarn workspace tailwindcss-elevation test-reporter
12 | after_script:
13 | - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
14 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tailwindcss-elevation
2 | [](https://travis-ci.com/jonaskay/tailwindcss-elevation) [](https://codeclimate.com/github/jonaskay/tailwindcss-elevation/test_coverage)
3 |
4 | Add Material Components elevation classes to your [Tailwind CSS](https://tailwindcss.com/docs/what-is-tailwind/) project. Check out the [demo](https://jonaskay.github.io/tailwindcss-elevation/)!
5 |
6 | ## Installation
7 |
8 | To install the package, run
9 |
10 | npm install tailwindcss-elevation
11 |
12 | To activate the plugin, add a `tailwind.config.js` file to the root of your project:
13 |
14 | ```javascript
15 | module.exports = {
16 | plugins: [
17 | require('tailwindcss-elevation'),
18 | ]
19 | }
20 | ```
21 |
22 | To learn more about configuring your Tailwind CSS installation, see https://tailwindcss.com/docs/configuration.
23 |
24 | ### Configuration
25 |
26 | To change the default configurations, you can include an options object:
27 |
28 | ```javascript
29 | module.exports = {
30 | plugins: [
31 | require('tailwindcss-elevation')(
32 | {
33 | color: '77,192,181',
34 | opacityBoost: '0.23'
35 | }
36 | )
37 | ]
38 | }
39 | ```
40 |
41 | Options accept the following properties:
42 |
43 | * `color` changes the default box-shadow base color and accepts an RGB (e.g. `'77,192,181'`) or HEX triplet (e.g. `'#4dc0b5'`) as its value. When using a CSS custom property (variable) as the value, you have to use an RGB triplet.
44 | * `opacityBoost` is added to the default box-shadow opacity and accepts a number between 0.0 and 1.0
45 |
46 | ## Basic usage
47 |
48 | You can apply elevation to an element using the `.elevation-{z-value}` utilities.
49 |
50 | ```html
51 |
52 | ```
53 |
54 | The z values range from 0 to 24.
55 |
56 | ## Changelog
57 |
58 | You can find the changelog of the `tailwindcss-elevation` package [here](/tailwindcss-elevation/CHANGELOG.md).
59 |
60 | ## Material documentation
61 | * [Material Design: Elevation](https://material.io/design/environment/elevation.html)
62 | * [Material Components for the web](https://material.io/develop/web/)
63 |
--------------------------------------------------------------------------------
/demo/dist/styles.css:
--------------------------------------------------------------------------------
1 | /*
2 | ! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com
3 | *//*
4 | 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
5 | 2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
6 | */
7 |
8 | *,
9 | ::before,
10 | ::after {
11 | box-sizing: border-box; /* 1 */
12 | border-width: 0; /* 2 */
13 | border-style: solid; /* 2 */
14 | border-color: #e5e7eb; /* 2 */
15 | }
16 |
17 | ::before,
18 | ::after {
19 | --tw-content: '';
20 | }
21 |
22 | /*
23 | 1. Use a consistent sensible line-height in all browsers.
24 | 2. Prevent adjustments of font size after orientation changes in iOS.
25 | 3. Use a more readable tab size.
26 | 4. Use the user's configured `sans` font-family by default.
27 | 5. Use the user's configured `sans` font-feature-settings by default.
28 | */
29 |
30 | html {
31 | line-height: 1.5; /* 1 */
32 | -webkit-text-size-adjust: 100%; /* 2 */
33 | -moz-tab-size: 4; /* 3 */
34 | tab-size: 4; /* 3 */
35 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */
36 | font-feature-settings: normal; /* 5 */
37 | }
38 |
39 | /*
40 | 1. Remove the margin in all browsers.
41 | 2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
42 | */
43 |
44 | body {
45 | margin: 0; /* 1 */
46 | line-height: inherit; /* 2 */
47 | }
48 |
49 | /*
50 | 1. Add the correct height in Firefox.
51 | 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
52 | 3. Ensure horizontal rules are visible by default.
53 | */
54 |
55 | hr {
56 | height: 0; /* 1 */
57 | color: inherit; /* 2 */
58 | border-top-width: 1px; /* 3 */
59 | }
60 |
61 | /*
62 | Add the correct text decoration in Chrome, Edge, and Safari.
63 | */
64 |
65 | abbr:where([title]) {
66 | text-decoration: underline dotted;
67 | }
68 |
69 | /*
70 | Remove the default font size and weight for headings.
71 | */
72 |
73 | h1,
74 | h2,
75 | h3,
76 | h4,
77 | h5,
78 | h6 {
79 | font-size: inherit;
80 | font-weight: inherit;
81 | }
82 |
83 | /*
84 | Reset links to optimize for opt-in styling instead of opt-out.
85 | */
86 |
87 | a {
88 | color: inherit;
89 | text-decoration: inherit;
90 | }
91 |
92 | /*
93 | Add the correct font weight in Edge and Safari.
94 | */
95 |
96 | b,
97 | strong {
98 | font-weight: bolder;
99 | }
100 |
101 | /*
102 | 1. Use the user's configured `mono` font family by default.
103 | 2. Correct the odd `em` font sizing in all browsers.
104 | */
105 |
106 | code,
107 | kbd,
108 | samp,
109 | pre {
110 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */
111 | font-size: 1em; /* 2 */
112 | }
113 |
114 | /*
115 | Add the correct font size in all browsers.
116 | */
117 |
118 | small {
119 | font-size: 80%;
120 | }
121 |
122 | /*
123 | Prevent `sub` and `sup` elements from affecting the line height in all browsers.
124 | */
125 |
126 | sub,
127 | sup {
128 | font-size: 75%;
129 | line-height: 0;
130 | position: relative;
131 | vertical-align: baseline;
132 | }
133 |
134 | sub {
135 | bottom: -0.25em;
136 | }
137 |
138 | sup {
139 | top: -0.5em;
140 | }
141 |
142 | /*
143 | 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
144 | 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
145 | 3. Remove gaps between table borders by default.
146 | */
147 |
148 | table {
149 | text-indent: 0; /* 1 */
150 | border-color: inherit; /* 2 */
151 | border-collapse: collapse; /* 3 */
152 | }
153 |
154 | /*
155 | 1. Change the font styles in all browsers.
156 | 2. Remove the margin in Firefox and Safari.
157 | 3. Remove default padding in all browsers.
158 | */
159 |
160 | button,
161 | input,
162 | optgroup,
163 | select,
164 | textarea {
165 | font-family: inherit; /* 1 */
166 | font-size: 100%; /* 1 */
167 | font-weight: inherit; /* 1 */
168 | line-height: inherit; /* 1 */
169 | color: inherit; /* 1 */
170 | margin: 0; /* 2 */
171 | padding: 0; /* 3 */
172 | }
173 |
174 | /*
175 | Remove the inheritance of text transform in Edge and Firefox.
176 | */
177 |
178 | button,
179 | select {
180 | text-transform: none;
181 | }
182 |
183 | /*
184 | 1. Correct the inability to style clickable types in iOS and Safari.
185 | 2. Remove default button styles.
186 | */
187 |
188 | button,
189 | [type='button'],
190 | [type='reset'],
191 | [type='submit'] {
192 | -webkit-appearance: button; /* 1 */
193 | background-color: transparent; /* 2 */
194 | background-image: none; /* 2 */
195 | }
196 |
197 | /*
198 | Use the modern Firefox focus style for all focusable elements.
199 | */
200 |
201 | :-moz-focusring {
202 | outline: auto;
203 | }
204 |
205 | /*
206 | Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
207 | */
208 |
209 | :-moz-ui-invalid {
210 | box-shadow: none;
211 | }
212 |
213 | /*
214 | Add the correct vertical alignment in Chrome and Firefox.
215 | */
216 |
217 | progress {
218 | vertical-align: baseline;
219 | }
220 |
221 | /*
222 | Correct the cursor style of increment and decrement buttons in Safari.
223 | */
224 |
225 | ::-webkit-inner-spin-button,
226 | ::-webkit-outer-spin-button {
227 | height: auto;
228 | }
229 |
230 | /*
231 | 1. Correct the odd appearance in Chrome and Safari.
232 | 2. Correct the outline style in Safari.
233 | */
234 |
235 | [type='search'] {
236 | -webkit-appearance: textfield; /* 1 */
237 | outline-offset: -2px; /* 2 */
238 | }
239 |
240 | /*
241 | Remove the inner padding in Chrome and Safari on macOS.
242 | */
243 |
244 | ::-webkit-search-decoration {
245 | -webkit-appearance: none;
246 | }
247 |
248 | /*
249 | 1. Correct the inability to style clickable types in iOS and Safari.
250 | 2. Change font properties to `inherit` in Safari.
251 | */
252 |
253 | ::-webkit-file-upload-button {
254 | -webkit-appearance: button; /* 1 */
255 | font: inherit; /* 2 */
256 | }
257 |
258 | /*
259 | Add the correct display in Chrome and Safari.
260 | */
261 |
262 | summary {
263 | display: list-item;
264 | }
265 |
266 | /*
267 | Removes the default spacing and border for appropriate elements.
268 | */
269 |
270 | blockquote,
271 | dl,
272 | dd,
273 | h1,
274 | h2,
275 | h3,
276 | h4,
277 | h5,
278 | h6,
279 | hr,
280 | figure,
281 | p,
282 | pre {
283 | margin: 0;
284 | }
285 |
286 | fieldset {
287 | margin: 0;
288 | padding: 0;
289 | }
290 |
291 | legend {
292 | padding: 0;
293 | }
294 |
295 | ol,
296 | ul,
297 | menu {
298 | list-style: none;
299 | margin: 0;
300 | padding: 0;
301 | }
302 |
303 | /*
304 | Prevent resizing textareas horizontally by default.
305 | */
306 |
307 | textarea {
308 | resize: vertical;
309 | }
310 |
311 | /*
312 | 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
313 | 2. Set the default placeholder color to the user's configured gray 400 color.
314 | */
315 |
316 | input::placeholder,
317 | textarea::placeholder {
318 | opacity: 1; /* 1 */
319 | color: #9ca3af; /* 2 */
320 | }
321 |
322 | /*
323 | Set the default cursor for buttons.
324 | */
325 |
326 | button,
327 | [role="button"] {
328 | cursor: pointer;
329 | }
330 |
331 | /*
332 | Make sure disabled buttons don't get the pointer cursor.
333 | */
334 | :disabled {
335 | cursor: default;
336 | }
337 |
338 | /*
339 | 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
340 | 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
341 | This can trigger a poorly considered lint error in some tools but is included by design.
342 | */
343 |
344 | img,
345 | svg,
346 | video,
347 | canvas,
348 | audio,
349 | iframe,
350 | embed,
351 | object {
352 | display: block; /* 1 */
353 | vertical-align: middle; /* 2 */
354 | }
355 |
356 | /*
357 | Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
358 | */
359 |
360 | img,
361 | video {
362 | max-width: 100%;
363 | height: auto;
364 | }
365 |
366 | /* Make elements with the HTML hidden attribute stay hidden by default */
367 | [hidden] {
368 | display: none;
369 | }
370 | main {
371 | margin: 0 auto;
372 | max-width: 1024px;
373 | padding: 1rem;
374 | }
375 |
376 | header {
377 | display: flex;
378 | justify-content: space-between;
379 | margin: 2rem 0;
380 | }
381 |
382 | nav {
383 | align-self: center;
384 | }
385 |
386 | *, ::before, ::after {
387 | --tw-border-spacing-x: 0;
388 | --tw-border-spacing-y: 0;
389 | --tw-translate-x: 0;
390 | --tw-translate-y: 0;
391 | --tw-rotate: 0;
392 | --tw-skew-x: 0;
393 | --tw-skew-y: 0;
394 | --tw-scale-x: 1;
395 | --tw-scale-y: 1;
396 | --tw-pan-x: ;
397 | --tw-pan-y: ;
398 | --tw-pinch-zoom: ;
399 | --tw-scroll-snap-strictness: proximity;
400 | --tw-ordinal: ;
401 | --tw-slashed-zero: ;
402 | --tw-numeric-figure: ;
403 | --tw-numeric-spacing: ;
404 | --tw-numeric-fraction: ;
405 | --tw-ring-inset: ;
406 | --tw-ring-offset-width: 0px;
407 | --tw-ring-offset-color: #fff;
408 | --tw-ring-color: rgb(59 130 246 / 0.5);
409 | --tw-ring-offset-shadow: 0 0 #0000;
410 | --tw-ring-shadow: 0 0 #0000;
411 | --tw-shadow: 0 0 #0000;
412 | --tw-shadow-colored: 0 0 #0000;
413 | --tw-blur: ;
414 | --tw-brightness: ;
415 | --tw-contrast: ;
416 | --tw-grayscale: ;
417 | --tw-hue-rotate: ;
418 | --tw-invert: ;
419 | --tw-saturate: ;
420 | --tw-sepia: ;
421 | --tw-drop-shadow: ;
422 | --tw-backdrop-blur: ;
423 | --tw-backdrop-brightness: ;
424 | --tw-backdrop-contrast: ;
425 | --tw-backdrop-grayscale: ;
426 | --tw-backdrop-hue-rotate: ;
427 | --tw-backdrop-invert: ;
428 | --tw-backdrop-opacity: ;
429 | --tw-backdrop-saturate: ;
430 | --tw-backdrop-sepia: ;
431 | }
432 |
433 | ::backdrop {
434 | --tw-border-spacing-x: 0;
435 | --tw-border-spacing-y: 0;
436 | --tw-translate-x: 0;
437 | --tw-translate-y: 0;
438 | --tw-rotate: 0;
439 | --tw-skew-x: 0;
440 | --tw-skew-y: 0;
441 | --tw-scale-x: 1;
442 | --tw-scale-y: 1;
443 | --tw-pan-x: ;
444 | --tw-pan-y: ;
445 | --tw-pinch-zoom: ;
446 | --tw-scroll-snap-strictness: proximity;
447 | --tw-ordinal: ;
448 | --tw-slashed-zero: ;
449 | --tw-numeric-figure: ;
450 | --tw-numeric-spacing: ;
451 | --tw-numeric-fraction: ;
452 | --tw-ring-inset: ;
453 | --tw-ring-offset-width: 0px;
454 | --tw-ring-offset-color: #fff;
455 | --tw-ring-color: rgb(59 130 246 / 0.5);
456 | --tw-ring-offset-shadow: 0 0 #0000;
457 | --tw-ring-shadow: 0 0 #0000;
458 | --tw-shadow: 0 0 #0000;
459 | --tw-shadow-colored: 0 0 #0000;
460 | --tw-blur: ;
461 | --tw-brightness: ;
462 | --tw-contrast: ;
463 | --tw-grayscale: ;
464 | --tw-hue-rotate: ;
465 | --tw-invert: ;
466 | --tw-saturate: ;
467 | --tw-sepia: ;
468 | --tw-drop-shadow: ;
469 | --tw-backdrop-blur: ;
470 | --tw-backdrop-brightness: ;
471 | --tw-backdrop-contrast: ;
472 | --tw-backdrop-grayscale: ;
473 | --tw-backdrop-hue-rotate: ;
474 | --tw-backdrop-invert: ;
475 | --tw-backdrop-opacity: ;
476 | --tw-backdrop-saturate: ;
477 | --tw-backdrop-sepia: ;
478 | }
479 | .demo {
480 | display: flex;
481 | flex-wrap: wrap;
482 | justify-content: center;
483 | }
484 | .card {
485 | margin: 1rem;
486 | padding: 2rem .5rem;
487 | text-align: center;
488 | width: 150px;
489 | }
490 | .text-blue-600 {
491 | --tw-text-opacity: 1;
492 | color: rgb(37 99 235 / var(--tw-text-opacity));
493 | }
494 | .underline {
495 | text-decoration-line: underline;
496 | }
497 | .elevation-0 {
498 | box-shadow: 0px 0px 0px 0px rgba(77,192,181,0.70), 0px 0px 0px 0px rgba(77,192,181,0.64), 0px 0px 0px 0px rgba(77,192,181,0.62);
499 | }
500 | .elevation-1 {
501 | box-shadow: 0px 2px 1px -1px rgba(77,192,181,0.70), 0px 1px 1px 0px rgba(77,192,181,0.64), 0px 1px 3px 0px rgba(77,192,181,0.62);
502 | }
503 | .elevation-2 {
504 | box-shadow: 0px 3px 1px -2px rgba(77,192,181,0.70), 0px 2px 2px 0px rgba(77,192,181,0.64), 0px 1px 5px 0px rgba(77,192,181,0.62);
505 | }
506 | .elevation-3 {
507 | box-shadow: 0px 3px 3px -2px rgba(77,192,181,0.70), 0px 3px 4px 0px rgba(77,192,181,0.64), 0px 1px 8px 0px rgba(77,192,181,0.62);
508 | }
509 | .elevation-4 {
510 | box-shadow: 0px 2px 4px -1px rgba(77,192,181,0.70), 0px 4px 5px 0px rgba(77,192,181,0.64), 0px 1px 10px 0px rgba(77,192,181,0.62);
511 | }
512 | .elevation-5 {
513 | box-shadow: 0px 3px 5px -1px rgba(77,192,181,0.70), 0px 5px 8px 0px rgba(77,192,181,0.64), 0px 1px 14px 0px rgba(77,192,181,0.62);
514 | }
515 | .elevation-6 {
516 | box-shadow: 0px 3px 5px -1px rgba(77,192,181,0.70), 0px 6px 10px 0px rgba(77,192,181,0.64), 0px 1px 18px 0px rgba(77,192,181,0.62);
517 | }
518 | .elevation-7 {
519 | box-shadow: 0px 4px 5px -2px rgba(77,192,181,0.70), 0px 7px 10px 1px rgba(77,192,181,0.64), 0px 2px 16px 1px rgba(77,192,181,0.62);
520 | }
521 | .elevation-8 {
522 | box-shadow: 0px 5px 5px -3px rgba(77,192,181,0.70), 0px 8px 10px 1px rgba(77,192,181,0.64), 0px 3px 14px 2px rgba(77,192,181,0.62);
523 | }
524 | .elevation-9 {
525 | box-shadow: 0px 5px 6px -3px rgba(77,192,181,0.70), 0px 9px 12px 1px rgba(77,192,181,0.64), 0px 3px 16px 2px rgba(77,192,181,0.62);
526 | }
527 | .elevation-10 {
528 | box-shadow: 0px 6px 6px -3px rgba(77,192,181,0.70), 0px 10px 14px 1px rgba(77,192,181,0.64), 0px 4px 18px 3px rgba(77,192,181,0.62);
529 | }
530 | .elevation-11 {
531 | box-shadow: 0px 6px 7px -4px rgba(77,192,181,0.70), 0px 11px 15px 1px rgba(77,192,181,0.64), 0px 4px 20px 3px rgba(77,192,181,0.62);
532 | }
533 | .elevation-12 {
534 | box-shadow: 0px 7px 8px -4px rgba(77,192,181,0.70), 0px 12px 17px 2px rgba(77,192,181,0.64), 0px 5px 22px 4px rgba(77,192,181,0.62);
535 | }
536 | .elevation-13 {
537 | box-shadow: 0px 7px 8px -4px rgba(77,192,181,0.70), 0px 13px 19px 2px rgba(77,192,181,0.64), 0px 5px 24px 4px rgba(77,192,181,0.62);
538 | }
539 | .elevation-14 {
540 | box-shadow: 0px 7px 9px -4px rgba(77,192,181,0.70), 0px 14px 21px 2px rgba(77,192,181,0.64), 0px 5px 26px 4px rgba(77,192,181,0.62);
541 | }
542 | .elevation-15 {
543 | box-shadow: 0px 8px 9px -5px rgba(77,192,181,0.70), 0px 15px 22px 2px rgba(77,192,181,0.64), 0px 6px 28px 5px rgba(77,192,181,0.62);
544 | }
545 | .elevation-16 {
546 | box-shadow: 0px 8px 10px -5px rgba(77,192,181,0.70), 0px 16px 24px 2px rgba(77,192,181,0.64), 0px 6px 30px 5px rgba(77,192,181,0.62);
547 | }
548 | .elevation-17 {
549 | box-shadow: 0px 8px 11px -5px rgba(77,192,181,0.70), 0px 17px 26px 2px rgba(77,192,181,0.64), 0px 6px 32px 5px rgba(77,192,181,0.62);
550 | }
551 | .elevation-18 {
552 | box-shadow: 0px 9px 11px -5px rgba(77,192,181,0.70), 0px 18px 28px 2px rgba(77,192,181,0.64), 0px 7px 34px 6px rgba(77,192,181,0.62);
553 | }
554 | .elevation-19 {
555 | box-shadow: 0px 9px 12px -6px rgba(77,192,181,0.70), 0px 19px 29px 2px rgba(77,192,181,0.64), 0px 7px 36px 6px rgba(77,192,181,0.62);
556 | }
557 | .elevation-20 {
558 | box-shadow: 0px 10px 13px -6px rgba(77,192,181,0.70), 0px 20px 31px 3px rgba(77,192,181,0.64), 0px 8px 38px 7px rgba(77,192,181,0.62);
559 | }
560 | .elevation-21 {
561 | box-shadow: 0px 10px 13px -6px rgba(77,192,181,0.70), 0px 21px 33px 3px rgba(77,192,181,0.64), 0px 8px 40px 7px rgba(77,192,181,0.62);
562 | }
563 | .elevation-22 {
564 | box-shadow: 0px 10px 14px -6px rgba(77,192,181,0.70), 0px 22px 35px 3px rgba(77,192,181,0.64), 0px 8px 42px 7px rgba(77,192,181,0.62);
565 | }
566 | .elevation-23 {
567 | box-shadow: 0px 11px 14px -7px rgba(77,192,181,0.70), 0px 23px 36px 3px rgba(77,192,181,0.64), 0px 9px 44px 8px rgba(77,192,181,0.62);
568 | }
569 | .elevation-24 {
570 | box-shadow: 0px 11px 15px -7px rgba(77,192,181,0.70), 0px 24px 38px 3px rgba(77,192,181,0.64), 0px 9px 46px 8px rgba(77,192,181,0.62);
571 | }
572 |
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "private": true,
7 | "devDependencies": {
8 | "postcss-cli": "^10.1.0",
9 | "tailwindcss": "^3.2.7",
10 | "tailwindcss-elevation": "*"
11 | },
12 | "scripts": {
13 | "build": "postcss src/styles.css -o dist/styles.css"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/demo/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/demo/src/styles.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | main {
7 | margin: 0 auto;
8 | max-width: 1024px;
9 | padding: 1rem;
10 | }
11 |
12 | header {
13 | display: flex;
14 | justify-content: space-between;
15 | margin: 2rem 0;
16 | }
17 |
18 | nav {
19 | align-self: center;
20 | }
21 | }
22 |
23 | @layer components {
24 | .demo {
25 | display: flex;
26 | flex-wrap: wrap;
27 | justify-content: center;
28 | }
29 |
30 | .card {
31 | margin: 1rem;
32 | padding: 2rem .5rem;
33 | text-align: center;
34 | width: 150px;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/demo/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ["../index.html"],
3 | plugins: [require("tailwindcss-elevation")],
4 | };
5 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | tailwindcss-elevation
8 |
9 |
10 |
11 |
12 |
20 | Demo
21 |
22 |
.elevation-0
23 |
.elevation-1
24 |
.elevation-2
25 |
.elevation-3
26 |
.elevation-4
27 |
.elevation-5
28 |
.elevation-6
29 |
.elevation-7
30 |
.elevation-8
31 |
.elevation-9
32 |
.elevation-10
33 |
.elevation-11
34 |
.elevation-12
35 |
.elevation-13
36 |
.elevation-14
37 |
.elevation-15
38 |
.elevation-16
39 |
.elevation-17
40 |
.elevation-18
41 |
.elevation-19
42 |
.elevation-20
43 |
.elevation-21
44 |
.elevation-22
45 |
.elevation-23
46 |
.elevation-24
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "workspaces": [
3 | "demo",
4 | "tailwindcss-elevation"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*.html
2 | **/*.css
3 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | commonjs: true,
4 | es6: true,
5 | mocha: true,
6 | node: true,
7 | },
8 | extends: ["standard", "plugin:prettier/recommended"],
9 | globals: {
10 | Atomics: "readonly",
11 | SharedArrayBuffer: "readonly",
12 | },
13 | parserOptions: {
14 | ecmaVersion: 2018,
15 | },
16 | rules: {},
17 | };
18 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## [Unreleased]
9 |
10 | ### Changed
11 |
12 | - Update tailwindcss version
13 | - Move to tailwindcss/plugin
14 |
15 | ### Security
16 |
17 | - Update dependencies to fix security vulnerabilities
18 |
19 | ## [1.0.1] - 2020-12-06
20 |
21 | ### Fixed
22 |
23 | - Add README and CHANGELOG to package
24 |
25 | ## [1.0.0] - 2020-12-06
26 |
27 | ### Changed
28 |
29 | - Add tailwindcss to peer dependencies
30 | - Update tailwindcss version
31 | - Remove test files from the package
32 |
33 | ### Security
34 |
35 | - Update dependencies to fix security vulnerabilities
36 |
37 | ## [0.3.5] - 2020-08-08
38 |
39 | ### Security
40 |
41 | - Update dependencies to fix security vulnerabilities
42 |
43 | ## [0.3.4] - 2020-04-03
44 |
45 | ### Security
46 |
47 | - Update dependencies to fix security vulnerabilities
48 |
49 | ## [0.3.3] - 2019-09-04
50 |
51 | ### Fixed
52 |
53 | - Allow CSS custom properties for color config
54 |
55 | ## [0.3.2] - 2019-08-20
56 |
57 | ### Changed
58 |
59 | - Allow HEX values for color config
60 |
61 | ## [0.3.1] - 2019-07-13
62 |
63 | ### Changed
64 |
65 | - Update tailwindcss version
66 |
67 | ### Security
68 |
69 | - Update dependencies to fix security vulnerabilities
70 |
71 | ## [0.3.0] - 2019-05-11
72 |
73 | ### Added
74 |
75 | - Opacity config
76 |
77 | ## [0.2.0] - 2019-04-17
78 |
79 | ### Added
80 |
81 | - Color config
82 |
83 | ## [0.1.1] - 2019-03-29
84 |
85 | ### Fixed
86 |
87 | - Removed extra `;` after CSS rule declarations
88 |
89 | ## [0.1.0] - 2019-03-19
90 |
91 | ### Added
92 |
93 | - Utility classes for elevation
94 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/README.md:
--------------------------------------------------------------------------------
1 | # tailwindcss-elevation
2 | [](https://travis-ci.com/jonaskay/tailwindcss-elevation) [](https://codeclimate.com/github/jonaskay/tailwindcss-elevation/test_coverage)
3 |
4 | Add Material Components elevation classes to your [Tailwind CSS](https://tailwindcss.com/docs/what-is-tailwind/) project. Check out the [demo](https://jonaskay.github.io/tailwindcss-elevation/)!
5 |
6 | ## Installation
7 |
8 | To install the package, run
9 |
10 | npm install tailwindcss-elevation
11 |
12 | To activate the plugin, add a `tailwind.config.js` file to the root of your project:
13 |
14 | ```javascript
15 | module.exports = {
16 | plugins: [
17 | require('tailwindcss-elevation'),
18 | ]
19 | }
20 | ```
21 |
22 | To learn more about configuring your Tailwind CSS installation, see https://tailwindcss.com/docs/configuration.
23 |
24 | ### Configuration
25 |
26 | To change the default configurations, you can include an options object:
27 |
28 | ```javascript
29 | module.exports = {
30 | plugins: [
31 | require('tailwindcss-elevation')(
32 | {
33 | color: '77,192,181',
34 | opacityBoost: '0.23'
35 | }
36 | )
37 | ]
38 | }
39 | ```
40 |
41 | Options accept the following properties:
42 |
43 | * `color` changes the default box-shadow base color and accepts an RGB (e.g. `'77,192,181'`) or HEX triplet (e.g. `'#4dc0b5'`) as its value. When using a CSS custom property (variable) as the value, you have to use an RGB triplet.
44 | * `opacityBoost` is added to the default box-shadow opacity and accepts a number between 0.0 and 1.0
45 |
46 | ## Basic usage
47 |
48 | You can apply elevation to an element using the `.elevation-{z-value}` utilities.
49 |
50 | ```html
51 |
52 | ```
53 |
54 | The z values range from 0 to 24.
55 |
56 | ## Material documentation
57 | * [Material Design: Elevation](https://material.io/design/environment/elevation.html)
58 | * [Material Components for the web](https://material.io/develop/web/)
59 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/index.js:
--------------------------------------------------------------------------------
1 | const plugin = require("tailwindcss/plugin");
2 |
3 | const validateOptions = require("./src/validate-options");
4 | const utilities = require("./src/utilities");
5 |
6 | module.exports = plugin.withOptions(function (options = {}) {
7 | return function ({ addUtilities }) {
8 | const err = validateOptions(options);
9 | if (err) {
10 | throw err;
11 | }
12 |
13 | addUtilities(utilities(options));
14 | };
15 | });
16 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tailwindcss-elevation",
3 | "version": "2.0.0",
4 | "description": "Tailwind CSS plugin for Material Components elevation classes.",
5 | "author": "Joonas Kykkänen ",
6 | "repository": "https://github.com/jonaskay/tailwindcss-elevation.git",
7 | "main": "index.js",
8 | "license": "MIT",
9 | "scripts": {
10 | "lint": "eslint index.js src/** test/**",
11 | "test": "mocha",
12 | "test-reporter": "nyc --reporter=lcov mocha"
13 | },
14 | "files": [
15 | "README.md",
16 | "CHANGELOG.md",
17 | "index.js",
18 | "src/"
19 | ],
20 | "devDependencies": {
21 | "autoprefixer": "^10.0.4",
22 | "chai": "^4.3.7",
23 | "chai-fs": "^2.0.0",
24 | "eslint": "^7.15.0",
25 | "eslint-config-prettier": "^6.4.0",
26 | "eslint-config-standard": "^14.1.0",
27 | "eslint-plugin-import": "^2.27.5",
28 | "eslint-plugin-node": "^11.0.0",
29 | "eslint-plugin-prettier": "^3.1.1",
30 | "eslint-plugin-promise": "^4.2.1",
31 | "eslint-plugin-standard": "^4.0.1",
32 | "husky": ">=1",
33 | "lint-staged": ">=8",
34 | "mocha": "^8.1.1",
35 | "nyc": "^15.0.0",
36 | "postcss": "^8.2.13",
37 | "postcss-cli": "^10.1.0",
38 | "prettier": "2.2.1"
39 | },
40 | "dependencies": {
41 | "hex-rgb": "^4.1.0"
42 | },
43 | "peerDependencies": {
44 | "tailwindcss": "^3.0.1"
45 | },
46 | "husky": {
47 | "hooks": {
48 | "pre-commit": "lint-staged"
49 | }
50 | },
51 | "lint-staged": {
52 | "*.js": [
53 | "eslint --fix",
54 | "git add"
55 | ]
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/src/box-shadow.js:
--------------------------------------------------------------------------------
1 | const hexToRgb = require("hex-rgb");
2 |
3 | const config = require("./config");
4 | const regex = require("./regex");
5 |
6 | module.exports = function boxShadow(z, options) {
7 | function shadow(type) {
8 | function isHex(value) {
9 | const re = new RegExp(regex.hex);
10 | return re.test(value);
11 | }
12 |
13 | function getRGBTriplet(value) {
14 | if (isHex(value)) {
15 | const rgb = hexToRgb(value);
16 | return [rgb.red, rgb.green, rgb.blue].join(",");
17 | }
18 | return value;
19 | }
20 |
21 | function calculateOpacity(boost) {
22 | const add = parseFloat(boost) || 0;
23 | const base = parseFloat(config.opacity[type]);
24 |
25 | return (base + add).toFixed(2);
26 | }
27 |
28 | const offset = config.elevation[type][z];
29 | const color = (options && getRGBTriplet(options.color)) || "0,0,0";
30 | const opacity = calculateOpacity(options && options.opacityBoost);
31 |
32 | return `${offset} rgba(${color},${opacity})`;
33 | }
34 |
35 | if (typeof z !== "number") {
36 | return;
37 | }
38 | if (z < 0 || z > 24) {
39 | return;
40 | }
41 |
42 | return [shadow("umbra"), shadow("penumbra"), shadow("ambient")].join(", ");
43 | };
44 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/src/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | elevation: {
3 | umbra: {
4 | 0: "0px 0px 0px 0px",
5 | 1: "0px 2px 1px -1px",
6 | 2: "0px 3px 1px -2px",
7 | 3: "0px 3px 3px -2px",
8 | 4: "0px 2px 4px -1px",
9 | 5: "0px 3px 5px -1px",
10 | 6: "0px 3px 5px -1px",
11 | 7: "0px 4px 5px -2px",
12 | 8: "0px 5px 5px -3px",
13 | 9: "0px 5px 6px -3px",
14 | 10: "0px 6px 6px -3px",
15 | 11: "0px 6px 7px -4px",
16 | 12: "0px 7px 8px -4px",
17 | 13: "0px 7px 8px -4px",
18 | 14: "0px 7px 9px -4px",
19 | 15: "0px 8px 9px -5px",
20 | 16: "0px 8px 10px -5px",
21 | 17: "0px 8px 11px -5px",
22 | 18: "0px 9px 11px -5px",
23 | 19: "0px 9px 12px -6px",
24 | 20: "0px 10px 13px -6px",
25 | 21: "0px 10px 13px -6px",
26 | 22: "0px 10px 14px -6px",
27 | 23: "0px 11px 14px -7px",
28 | 24: "0px 11px 15px -7px",
29 | },
30 | penumbra: {
31 | 0: "0px 0px 0px 0px",
32 | 1: "0px 1px 1px 0px",
33 | 2: "0px 2px 2px 0px",
34 | 3: "0px 3px 4px 0px",
35 | 4: "0px 4px 5px 0px",
36 | 5: "0px 5px 8px 0px",
37 | 6: "0px 6px 10px 0px",
38 | 7: "0px 7px 10px 1px",
39 | 8: "0px 8px 10px 1px",
40 | 9: "0px 9px 12px 1px",
41 | 10: "0px 10px 14px 1px",
42 | 11: "0px 11px 15px 1px",
43 | 12: "0px 12px 17px 2px",
44 | 13: "0px 13px 19px 2px",
45 | 14: "0px 14px 21px 2px",
46 | 15: "0px 15px 22px 2px",
47 | 16: "0px 16px 24px 2px",
48 | 17: "0px 17px 26px 2px",
49 | 18: "0px 18px 28px 2px",
50 | 19: "0px 19px 29px 2px",
51 | 20: "0px 20px 31px 3px",
52 | 21: "0px 21px 33px 3px",
53 | 22: "0px 22px 35px 3px",
54 | 23: "0px 23px 36px 3px",
55 | 24: "0px 24px 38px 3px",
56 | },
57 | ambient: {
58 | 0: "0px 0px 0px 0px",
59 | 1: "0px 1px 3px 0px",
60 | 2: "0px 1px 5px 0px",
61 | 3: "0px 1px 8px 0px",
62 | 4: "0px 1px 10px 0px",
63 | 5: "0px 1px 14px 0px",
64 | 6: "0px 1px 18px 0px",
65 | 7: "0px 2px 16px 1px",
66 | 8: "0px 3px 14px 2px",
67 | 9: "0px 3px 16px 2px",
68 | 10: "0px 4px 18px 3px",
69 | 11: "0px 4px 20px 3px",
70 | 12: "0px 5px 22px 4px",
71 | 13: "0px 5px 24px 4px",
72 | 14: "0px 5px 26px 4px",
73 | 15: "0px 6px 28px 5px",
74 | 16: "0px 6px 30px 5px",
75 | 17: "0px 6px 32px 5px",
76 | 18: "0px 7px 34px 6px",
77 | 19: "0px 7px 36px 6px",
78 | 20: "0px 8px 38px 7px",
79 | 21: "0px 8px 40px 7px",
80 | 22: "0px 8px 42px 7px",
81 | 23: "0px 9px 44px 8px",
82 | 24: "0px 9px 46px 8px",
83 | },
84 | },
85 |
86 | opacity: {
87 | umbra: ".2",
88 | penumbra: ".14",
89 | ambient: ".12",
90 | },
91 | };
92 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/src/regex.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | customProperty: "var([^)]+)",
3 | hex: "#[A-Fa-f0-9]+",
4 | rgb: "\\s*\\d+\\s*\\,\\s*\\d+\\s*\\,\\s*\\d+\\s*",
5 | };
6 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/src/utilities.js:
--------------------------------------------------------------------------------
1 | const boxShadow = require("./box-shadow");
2 |
3 | module.exports = function utilities(config = {}) {
4 | const result = {};
5 |
6 | for (let i = 0; i < 25; i++) {
7 | result[`.elevation-${i}`] = { boxShadow: boxShadow(i, config) };
8 | }
9 |
10 | return result;
11 | };
12 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/src/validate-options.js:
--------------------------------------------------------------------------------
1 | const regex = require("./regex");
2 |
3 | function validateColor(color) {
4 | if (!color) {
5 | return null;
6 | }
7 |
8 | const re = new RegExp(`${regex.rgb}|${regex.hex}|${regex.customProperty}`);
9 | if (re.test(color)) {
10 | return null;
11 | } else {
12 | return new Error(`Invalid color value: ${color}`);
13 | }
14 | }
15 |
16 | function validateOpacityBoost(opacityBoost) {
17 | if (!opacityBoost) {
18 | return null;
19 | }
20 |
21 | const err = new Error(`Invalid opacityBoost value: ${opacityBoost}`);
22 | const num = parseFloat(opacityBoost);
23 | if (isNaN(num)) {
24 | return err;
25 | }
26 | if (num < 0 || num > 1) {
27 | return err;
28 | }
29 |
30 | return null;
31 | }
32 |
33 | module.exports = function validateOptions(options) {
34 | if (!options) {
35 | return null;
36 | }
37 |
38 | const error =
39 | validateColor(options.color) || validateOpacityBoost(options.opacityBoost);
40 | if (!error) {
41 | return null;
42 | }
43 |
44 | return error;
45 | };
46 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/test/box-shadow.test.js:
--------------------------------------------------------------------------------
1 | const assert = require("chai").assert;
2 |
3 | const boxShadow = require("../src/box-shadow");
4 |
5 | describe("#boxShadow()", function () {
6 | it("should return undefined when z is not present", function () {
7 | assert.isUndefined(boxShadow());
8 | });
9 |
10 | it("should return undefined when z is not a number", function () {
11 | assert.isUndefined(boxShadow("1"));
12 | });
13 |
14 | it("should return undefined when z is less than 0", function () {
15 | assert.isUndefined(boxShadow(-1));
16 | });
17 |
18 | it("should return undefined when z is greater than 24", function () {
19 | assert.isUndefined(boxShadow(25));
20 | });
21 |
22 | it("should return valid box-shadow value when z is 0", function () {
23 | assert.equal(
24 | boxShadow(0),
25 | "0px 0px 0px 0px rgba(0,0,0,0.20), 0px 0px 0px 0px rgba(0,0,0,0.14), 0px 0px 0px 0px rgba(0,0,0,0.12)"
26 | );
27 | });
28 |
29 | it("should return valid box-shadow value when z is 1", function () {
30 | assert.equal(
31 | boxShadow(1),
32 | "0px 2px 1px -1px rgba(0,0,0,0.20), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12)"
33 | );
34 | });
35 |
36 | it("should return valid box-shadow value when z is 24", function () {
37 | assert.equal(
38 | boxShadow(24),
39 | "0px 11px 15px -7px rgba(0,0,0,0.20), 0px 24px 38px 3px rgba(0,0,0,0.14), 0px 9px 46px 8px rgba(0,0,0,0.12)"
40 | );
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/test/fixtures/default-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ["./test/fixtures/example.html"],
3 | plugins: [require("../../index")],
4 | };
5 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/test/fixtures/example.html:
--------------------------------------------------------------------------------
1 |
2 | Hello, World!
3 | Hello, World!
4 | Hello, World!
5 | Hello, World!
6 | Hello, World!
7 | Hello, World!
8 | Hello, World!
9 | Hello, World!
10 | Hello, World!
11 | Hello, World!
12 | Hello, World!
13 | Hello, World!
14 | Hello, World!
15 | Hello, World!
16 | Hello, World!
17 | Hello, World!
18 | Hello, World!
19 | Hello, World!
20 | Hello, World!
21 | Hello, World!
22 | Hello, World!
23 | Hello, World!
24 | Hello, World!
25 | Hello, World!
26 | Hello, World!
27 | Hello, World!
28 | Hello, World!
29 | Hello, World!
30 | Hello, World!
31 | Hello, World!
32 | Hello, World!
33 | Hello, World!
34 | Hello, World!
35 | Hello, World!
36 | Hello, World!
37 | Hello, World!
38 | Hello, World!
39 | Hello, World!
40 | Hello, World!
41 | Hello, World!
42 | Hello, World!
43 | Hello, World!
44 | Hello, World!
45 | Hello, World!
46 | Hello, World!
47 | Hello, World!
48 | Hello, World!
49 | Hello, World!
50 | Hello, World!
51 | Hello, World!
52 |
53 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/test/fixtures/hex-color-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ["./test/fixtures/example.html"],
3 | plugins: [require("../../index")({ color: "#4FD1C5" })],
4 | };
5 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/test/fixtures/invalid-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ["./test/fixtures/example.html"],
3 | plugins: [
4 | require("../../index")({ color: "invalid", opacityBoost: "invalid" }),
5 | ],
6 | };
7 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/test/fixtures/opacity-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ["./test/fixtures/example.html"],
3 | plugins: [require("../../index")({ opacityBoost: "0.1" })],
4 | };
5 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/test/fixtures/rgb-color-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ["./test/fixtures/example.html"],
3 | plugins: [require("../../index")({ color: "255,0,0" })],
4 | };
5 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/test/fixtures/styles.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/test/fixtures/var-color-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ["./test/fixtures/example.html"],
3 | plugins: [require("../../index")({ color: "var(--color)" })],
4 | };
5 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/test/tailwind-build.test.js:
--------------------------------------------------------------------------------
1 | const exec = require("child_process").exec;
2 | const fs = require("fs");
3 | const chai = require("chai");
4 |
5 | const assert = chai.assert;
6 | chai.use(require("chai-fs"));
7 |
8 | const tmpDir = "./tmp";
9 | if (!fs.existsSync(tmpDir)) {
10 | fs.mkdirSync(tmpDir);
11 | }
12 |
13 | const source = "./test/fixtures/styles.css";
14 | const output = `${tmpDir}/styles.css`;
15 | const postcssConfig = `${tmpDir}/postcss.config.js`;
16 |
17 | function addPostcssConfig(tailwindConfig) {
18 | const content = `
19 | module.exports = {
20 | plugins: {
21 | tailwindcss: { config: '${tailwindConfig}' }
22 | },
23 | }
24 | `;
25 |
26 | return new Promise(function (resolve, reject) {
27 | fs.writeFile(postcssConfig, content, function (err) {
28 | if (err) {
29 | reject(new Error(`Failed to create ${postcssConfig}: ${err}`));
30 | }
31 |
32 | resolve();
33 | });
34 | });
35 | }
36 |
37 | async function buildCSSFile(config) {
38 | await addPostcssConfig(config);
39 | const cmd = `../node_modules/.bin/postcss ${source} -o ${output} --config ${postcssConfig}`;
40 |
41 | return new Promise(function (resolve, reject) {
42 | exec(cmd, function (err) {
43 | if (err) {
44 | reject(new Error(err));
45 | }
46 | resolve();
47 | });
48 | });
49 | }
50 |
51 | describe("tailwind", function () {
52 | describe("build", function () {
53 | this.timeout(5000);
54 |
55 | it("should generate CSS file with utilities", async function () {
56 | await buildCSSFile("./test/fixtures/default-config.js");
57 |
58 | const regexps = [
59 | /.elevation-0\s+{/g,
60 | /.sm\\:elevation-0\s+/g,
61 | /box-shadow: 0px 0px 0px 0px rgba\(0,0,0,0.20\), 0px 0px 0px 0px rgba\(0,0,0,0.14\), 0px 0px 0px 0px rgba\(0,0,0,0.12\);\s+/g,
62 | ];
63 | regexps.forEach(function (regexp) {
64 | assert.fileContentMatch(output, regexp);
65 | });
66 | });
67 |
68 | it("should generate CSS file with utilities when base color is defined using an RGB triplet", async function () {
69 | await buildCSSFile("./test/fixtures/rgb-color-config.js");
70 |
71 | const regexps = [
72 | /.elevation-0\s+{/g,
73 | /box-shadow: 0px 0px 0px 0px rgba\(255,0,0,0.20\), 0px 0px 0px 0px rgba\(255,0,0,0.14\), 0px 0px 0px 0px rgba\(255,0,0,0.12\);\s+/g,
74 | ];
75 | regexps.forEach(function (regexp) {
76 | assert.fileContentMatch(output, regexp);
77 | });
78 | });
79 |
80 | it("should generate CSS file with utilities when base color is defined using a HEX triplet", async function () {
81 | await buildCSSFile("./test/fixtures/hex-color-config.js");
82 |
83 | const regexps = [
84 | /.elevation-0\s+{/g,
85 | /box-shadow: 0px 0px 0px 0px rgba\(79,209,197,0.20\), 0px 0px 0px 0px rgba\(79,209,197,0.14\), 0px 0px 0px 0px rgba\(79,209,197,0.12\);\s+/g,
86 | ];
87 | regexps.forEach(function (regexp) {
88 | assert.fileContentMatch(output, regexp);
89 | });
90 | });
91 |
92 | it("should generate CSS file with utilities when base color is defined using a custom property", async function () {
93 | await buildCSSFile("./test/fixtures/var-color-config.js");
94 |
95 | const regexps = [
96 | /.elevation-0\s+{/g,
97 | /box-shadow: 0px 0px 0px 0px rgba\(var\(--color\),0.20\), 0px 0px 0px 0px rgba\(var\(--color\),0.14\), 0px 0px 0px 0px rgba\(var\(--color\),0.12\);\s+/g,
98 | ];
99 | regexps.forEach(function (regexp) {
100 | assert.fileContentMatch(output, regexp);
101 | });
102 | });
103 |
104 | it("should generate CSS file with utilities when opacity boost is defined", async function () {
105 | await buildCSSFile("./test/fixtures/opacity-config.js");
106 |
107 | assert.fileContentMatch(
108 | output,
109 | /box-shadow: 0px 0px 0px 0px rgba\(0,0,0,0.30\), 0px 0px 0px 0px rgba\(0,0,0,0.24\), 0px 0px 0px 0px rgba\(0,0,0,0.22\);\s+/g
110 | );
111 | });
112 |
113 | it("should error when config is invalid", async function () {
114 | try {
115 | await buildCSSFile("./test/fixtures/invalid-config.js");
116 | } catch (error) {
117 | assert.isOk(true);
118 | return;
119 | }
120 | assert.isOk(false);
121 | });
122 | });
123 | });
124 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/test/utilities.test.js:
--------------------------------------------------------------------------------
1 | const assert = require("chai").assert;
2 |
3 | const utilities = require("../src/utilities");
4 |
5 | describe("#utilities()", function () {
6 | it("should return a utility for .elevation-0", function () {
7 | assert.equal(
8 | utilities()[".elevation-0"].boxShadow,
9 | "0px 0px 0px 0px rgba(0,0,0,0.20), 0px 0px 0px 0px rgba(0,0,0,0.14), 0px 0px 0px 0px rgba(0,0,0,0.12)"
10 | );
11 | });
12 |
13 | it("should return a utility for .elevation-24", function () {
14 | assert.equal(
15 | utilities()[".elevation-24"].boxShadow,
16 | "0px 11px 15px -7px rgba(0,0,0,0.20), 0px 24px 38px 3px rgba(0,0,0,0.14), 0px 9px 46px 8px rgba(0,0,0,0.12)"
17 | );
18 | });
19 |
20 | it("should return a utility for .elevation-1 when config includes color with an RGB triplet", function () {
21 | const config = { color: "255,255,0" };
22 |
23 | assert.equal(
24 | utilities(config)[".elevation-1"].boxShadow,
25 | "0px 2px 1px -1px rgba(255,255,0,0.20), 0px 1px 1px 0px rgba(255,255,0,0.14), 0px 1px 3px 0px rgba(255,255,0,0.12)"
26 | );
27 | });
28 |
29 | it("should return a utility for .elevation-1 when config includes color with a HEX triplet", function () {
30 | const config = { color: "#E53E3E" };
31 |
32 | assert.equal(
33 | utilities(config)[".elevation-1"].boxShadow,
34 | "0px 2px 1px -1px rgba(229,62,62,0.20), 0px 1px 1px 0px rgba(229,62,62,0.14), 0px 1px 3px 0px rgba(229,62,62,0.12)"
35 | );
36 | });
37 |
38 | it("should return a utility for .elevation-1 when config includes color with a custom property", function () {
39 | const config = { color: "var(--blue)" };
40 |
41 | assert.equal(
42 | utilities(config)[".elevation-1"].boxShadow,
43 | "0px 2px 1px -1px rgba(var(--blue),0.20), 0px 1px 1px 0px rgba(var(--blue),0.14), 0px 1px 3px 0px rgba(var(--blue),0.12)"
44 | );
45 | });
46 |
47 | it("should return a utility for .elevation-2 when config includes opacityBoost", function () {
48 | const config = { opacityBoost: ".23" };
49 | assert.equal(
50 | utilities(config)[".elevation-2"].boxShadow,
51 | "0px 3px 1px -2px rgba(0,0,0,0.43), 0px 2px 2px 0px rgba(0,0,0,0.37), 0px 1px 5px 0px rgba(0,0,0,0.35)"
52 | );
53 | });
54 |
55 | it("should return a utility for .elevation-3 when config includes color and opacityBoost", function () {
56 | const config = { color: "77,192,181", opacityBoost: "0.42" };
57 |
58 | assert.equal(
59 | utilities(config)[".elevation-3"].boxShadow,
60 | "0px 3px 3px -2px rgba(77,192,181,0.62), 0px 3px 4px 0px rgba(77,192,181,0.56), 0px 1px 8px 0px rgba(77,192,181,0.54)"
61 | );
62 | });
63 |
64 | it("should return undefined for .elevation-25", function () {
65 | assert.isUndefined(utilities()[".elevation-25"]);
66 | });
67 | });
68 |
--------------------------------------------------------------------------------
/tailwindcss-elevation/test/validate-options.test.js:
--------------------------------------------------------------------------------
1 | const assert = require("chai").assert;
2 |
3 | const validateOptions = require("../src/validate-options");
4 |
5 | describe("#validateOptions()", function () {
6 | it("should return an error message if color is invalid", function () {
7 | const colors = [
8 | "red",
9 | "1",
10 | "255,0",
11 | "-1,-2,-3",
12 | "4fd1c5",
13 | "#...",
14 | "var",
15 | "(--foo)",
16 | ];
17 | colors.forEach(function (color) {
18 | assert.equal(
19 | validateOptions({ color: color }).message,
20 | `Invalid color value: ${color}`
21 | );
22 | });
23 | });
24 |
25 | it("should return null if color is valid", function () {
26 | const colors = [
27 | "0, 0, 0",
28 | " 1,2,3 ",
29 | "255,255,255",
30 | "#4fd1c5",
31 | "#FFF",
32 | "var(--foo)",
33 | ];
34 | colors.forEach(function (color) {
35 | assert.isNull(validateOptions({ color: color }));
36 | });
37 | });
38 |
39 | it("should return an error message if opacity boost is invalid", function () {
40 | const opacityBoosts = ["foo", "1.01", "-0.01"];
41 | opacityBoosts.forEach(function (opacityBoost) {
42 | assert.equal(
43 | validateOptions({ opacityBoost: opacityBoost }).message,
44 | `Invalid opacityBoost value: ${opacityBoost}`
45 | );
46 | });
47 | });
48 |
49 | it("should return null if opacity boost is valid", function () {
50 | const opacityBoosts = ["0", "1", "0.0", "1.0", "0.01", ".99"];
51 | opacityBoosts.forEach(function (opacityBoost) {
52 | assert.isNull(validateOptions({ opacityBoost: opacityBoost }));
53 | });
54 | });
55 |
56 | it("should return null if options is empty", function () {
57 | assert.isNull(validateOptions({}));
58 | });
59 |
60 | it("should return null if options is missing", function () {
61 | assert.isNull(validateOptions());
62 | });
63 | });
64 |
--------------------------------------------------------------------------------