├── .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 | [![Build Status](https://travis-ci.com/jonaskay/tailwindcss-elevation.svg?branch=master)](https://travis-ci.com/jonaskay/tailwindcss-elevation) [![Test Coverage](https://api.codeclimate.com/v1/badges/0c8c89a338155da523e7/test_coverage)](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 |
13 |

tailwindcss-elevation

14 | 19 |
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 | [![Build Status](https://travis-ci.com/jonaskay/tailwindcss-elevation.svg?branch=master)](https://travis-ci.com/jonaskay/tailwindcss-elevation) [![Test Coverage](https://api.codeclimate.com/v1/badges/0c8c89a338155da523e7/test_coverage)](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 | --------------------------------------------------------------------------------