├── README.md ├── index.js ├── package.json ├── src └── plugin-scrims.js └── test ├── plugin-scrims.spec.js └── spec-helper.js /README.md: -------------------------------------------------------------------------------- 1 | # Tailwind CSS Scrims 2 | > Configurable Tailwind plugin for generating scrim utility classes. 3 | 4 | ## What are Scrims? 5 | 6 | In photography, a scrim is something that softens light — from the sun, or a flash — and is typically a piece of fabric, paper or acrylic stretched across a frame. 7 | 8 | In UX, a scrim is a design technique (a gradient or overlay) used to make text more legible when overlaid on an image. The kind of visual treatment that is becoming increasingly common in card-based design. 9 | 10 | A typical scrim is a neutral gradient that starts about half-way down an image and increases to 30%-40% opacity, creating a subtle darkening effect that provides enought contrast for white text to be legible. 11 | 12 | ## Install 13 | 14 | `tailwindcss-scrims` automatically generates generates scrim class variations for specified directions, distances and gradient densities. 15 | 16 | 17 | ``` 18 | npm install --save-dev tailwindcss-scrims 19 | ``` 20 | 21 | ## Usage 22 | 23 | ```js 24 | // tailwind.js 25 | module.exports = { 26 | // ... 27 | plugins: [ 28 | require('tailwindcss-scrims')({ 29 | directions: { 30 | 't': 'to bottom', 31 | 'b': 'to top', 32 | 'r': 'to left', 33 | 'l': 'to right', 34 | }, 35 | distances: { 36 | '1/4': '25%', 37 | '1/3': '33.33333%', 38 | '1/2': '50%', 39 | '2/3': '66.66666%', 40 | '3/4': '75%', 41 | }, 42 | colors: { 43 | default: [rgba(0,0,0,0.4), rgba(0,0,0,0)], 44 | }, 45 | variants: ['responsive', 'hover'], 46 | }) 47 | ] 48 | // ... 49 | } 50 | ``` 51 | 52 | ## Options 53 | 54 | ### `directions` (optional) 55 | 56 | **Default:** 57 | ```js 58 | { 59 | 't': 'to bottom', 60 | 'b': 'to top', 61 | 'r': 'to left', 62 | 'l': 'to right', 63 | } 64 | ``` 65 | 66 | This is where you can define the direction — or location of emphasis — for a scrim. i.e. where the scrim color is darkest. 67 | Each `direction` is specified as a key/value pair. The key is used in the utility class name, the value is a valid CSS Gradient direction. 68 | 69 | 70 | ### `distances` (optional) 71 | 72 | **Default:** 73 | ```js 74 | { 75 | '1/4': '25%', 76 | '1/3': '33.33333%', 77 | '1/2': '50%', 78 | '2/3': '66.66666%', 79 | '3/4': '75%', 80 | } 81 | ``` 82 | 83 | This defines the distance the scrim covers. i.e. how much of the image is covered by the scrim gradient. Each `distance` is specified as a key/value pair. The key is used in the utility class name, the value is a valid CSS Gradient 'stop' location. 84 | 85 | 86 | ### `colors` (optional) 87 | 88 | **Default:** 89 | ```js 90 | { 91 | default: [rgba(0,0,0,0.4), rgba(0,0,0,0)], 92 | } 93 | ``` 94 | 95 | `colors` define the `{start}` and `{end}` colors (including alpha) for a scrim. 96 | 97 | ### `variants` (optional) 98 | 99 | **Default:** 100 | ```js 101 | ['responsive', 'hover'] 102 | ``` 103 | 104 | As per the [tailwind plugin docs](https://tailwindcss.com/docs/plugins/) you can also pass variants (responsive, hover, etc.) as an option. 105 | 106 | ```js 107 | require('tailwindcss-scrims')({ 108 | variants: ['responsive', 'hover'], 109 | }) 110 | ``` 111 | 112 | ## Example 113 | 114 | This configuration: 115 | 116 | ```js 117 | // tailwind.js 118 | module.exports = { 119 | // ... 120 | plugins: [ 121 | require('tailwindcss-scrims')({ 122 | directions: { 123 | 't': 'to bottom', 124 | 'b': 'to top', 125 | }, 126 | distances: { 127 | default: '25%', 128 | '1/2': '50%', 129 | }, 130 | colors: { 131 | default: [rgba(0,0,0,0.4), rgba(0,0,0,0)], 132 | }, 133 | variants: [], 134 | }) 135 | ] 136 | // ... 137 | } 138 | ``` 139 | 140 | Results in the following utilities: 141 | 142 | ```css 143 | .scrim-t::after: { 144 | content: " ", 145 | position: absolute, 146 | top: 0, 147 | left: 0, 148 | width: 100%, 149 | height: 100%, 150 | backgroundImage: linear-gradient(to bottom, rgba(0,0,0,0.4), rgba(0,0,0,0) 25%); 151 | } 152 | 153 | .scrim-t-1\/2::after: { 154 | content: " ", 155 | position: absolute, 156 | top: 0, 157 | left: 0, 158 | width: 100%, 159 | height: 100%, 160 | backgroundImage: linear-gradient(to bottom, rgba(0,0,0,0.4), rgba(0,0,0,0) 50%); 161 | } 162 | 163 | .scrim-b::after: { 164 | content: " ", 165 | position: absolute, 166 | top: 0, 167 | left: 0, 168 | width: 100%, 169 | height: 100%, 170 | backgroundImage: linear-gradient(to top, rgba(0,0,0,0.4), rgba(0,0,0,0) 25%); 171 | } 172 | 173 | .scrim-b-1\/2::after: { 174 | content: " ", 175 | position: absolute, 176 | top: 0, 177 | left: 0, 178 | width: 100%, 179 | height: 100%, 180 | backgroundImage: linear-gradient(to top, rgba(0,0,0,0.4), rgba(0,0,0,0) 50%); 181 | } 182 | ``` 183 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const plugin = require('./src/plugin-scrims') 2 | 3 | module.exports = plugin -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tailwindcss-scrims", 3 | "version": "1.0.0", 4 | "description": "Tailwind CSS plugin to generate scrim classes.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/brettgullan/tailwindcss-scrims.git" 12 | }, 13 | "keywords": [ 14 | "tailwind", 15 | "tailwindcss", 16 | "tailwind-css", 17 | "tailwindcss-plugin" 18 | ], 19 | "author": "Brett Gullan", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/brettgullan/tailwindcss-scrims/issues" 23 | }, 24 | "homepage": "https://github.com/brettgullan/tailwindcss-scrims#readme", 25 | "dependencies": { 26 | "lodash.map": "^4.6.0", 27 | "ramda": "^0.25.0" 28 | }, 29 | "devDependencies": { 30 | "chai": "^4.1.2", 31 | "mocha": "^5.2.0", 32 | "sinon": "^6.3.4", 33 | "sinon-chai": "^3.2.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/plugin-scrims.js: -------------------------------------------------------------------------------- 1 | const map = require('lodash.map') 2 | const { compose, flatten, merge, mergeAll } = require('ramda') 3 | 4 | //----------------------------------------------------------------------------- 5 | 6 | const scrim = { 7 | content: '" "', 8 | position: 'absolute', 9 | top: 0, 10 | left: 0, 11 | width: '100%', 12 | height: '100%', 13 | } 14 | 15 | //----------------------------------------------------------------------------- 16 | 17 | let DIRECTIONS = { 18 | 'b': 'to top', 19 | 't': 'to bottom', 20 | 'l': 'to right', 21 | 'r': 'to left', 22 | } 23 | 24 | let DISTANCES = { 25 | 'default': '50%', 26 | '1/4': '25%', 27 | '1/3': '33.33333%', 28 | '1/2': '50%', 29 | '2/3': '66.66666%', 30 | '3/4': '75%', 31 | } 32 | 33 | let COLORS = { 34 | default: ['rgba(0,0,0,0.25)', 'rgba(0,0,0,0)'], 35 | } 36 | 37 | let VARIANTS = ['responsive', 'hover'] 38 | 39 | //----------------------------------------------------------------------------- 40 | 41 | module.exports = ({ 42 | directions, 43 | distances, 44 | colors, 45 | variants, 46 | } = {}) => { 47 | 48 | DIRECTIONS = directions || DIRECTIONS 49 | DISTANCES = distances || DISTANCES 50 | COLORS = colors || COLORS 51 | VARIANTS = variants || VARIANTS 52 | 53 | return ({ addUtilities, e }) => { 54 | const scrims = compose(mergeAll, flatten)( 55 | map(DIRECTIONS, (direction, k) => 56 | map(DISTANCES, (distance, l) => 57 | map(COLORS, (colors, m) => { 58 | const [ start, end ] = colors 59 | return { 60 | [`.scrim-${e(k)}${ l !== 'default' ? `-${e(l)}` : '' }${ m !== 'default' ? `-${e(m)}` : '' }`]: { 61 | '&::after': merge(scrim, { 62 | backgroundImage: `linear-gradient(${direction}, ${start}, ${end} ${distance})` 63 | }) 64 | } 65 | } 66 | }) 67 | ) 68 | ) 69 | ) 70 | 71 | addUtilities(scrims, { 72 | variants: VARIANTS, 73 | }) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/plugin-scrims.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars, no-undef, padded-blocks, import/no-dynamic-require */ 2 | const { 3 | identity, 4 | keys, 5 | } = require('ramda') 6 | 7 | const { expect, sinon } = require('./spec-helper') 8 | 9 | //----------------------------------------------------------------------------- 10 | 11 | const Plugin = require('../src/plugin-scrims') 12 | 13 | //----------------------------------------------------------------------------- 14 | 15 | describe('Tailwind Scrims Plugin:', () => { 16 | 17 | it('Exports a function', () => { 18 | expect(Plugin).to.be.a('function') 19 | }) 20 | 21 | it('Returns a plugin function when invoked', () => { 22 | const result = Plugin() 23 | expect(result).to.be.a('function') 24 | }) 25 | 26 | it('Calls addUtilities when invoked', () => { 27 | const addUtilities = sinon.stub() 28 | const e = identity 29 | const plugin = Plugin() 30 | plugin({ addUtilities, e }) 31 | 32 | expect(addUtilities).to.have.been.called 33 | }) 34 | 35 | it('Generates correct list of default classes', () => { 36 | const addUtilities = sinon.stub() 37 | const e = identity 38 | const plugin = Plugin() 39 | plugin({ addUtilities, e }) 40 | const [ utilities, options ] = addUtilities.getCall(0).args 41 | 42 | const result = keys(utilities) 43 | 44 | expect(result).to.have.lengthOf(24) 45 | 46 | expect(result).to.include.members([ 47 | '.scrim-b', 48 | '.scrim-l', 49 | '.scrim-r', 50 | '.scrim-t', 51 | ]) 52 | 53 | expect(result).to.include.members([ 54 | '.scrim-b-1/4', 55 | '.scrim-b-1/3', 56 | '.scrim-b-1/2', 57 | '.scrim-b-2/3', 58 | '.scrim-b-3/4', 59 | ]) 60 | }) 61 | 62 | 63 | it('Generates correct list of custom classes', () => { 64 | const addUtilities = sinon.stub() 65 | const e = identity 66 | const plugin = Plugin({ 67 | directions: { 68 | 'tr': 'to top right', 69 | 'bl': 'to bottom left', 70 | }, 71 | distances: { 72 | '10': '10%', 73 | '20': '20%', 74 | } 75 | }) 76 | plugin({ addUtilities, e }) 77 | const [ utilities, options ] = addUtilities.getCall(0).args 78 | 79 | const result = keys(utilities) 80 | 81 | expect(result).to.have.lengthOf(4) 82 | 83 | expect(result).to.include.members([ 84 | '.scrim-tr-10', 85 | '.scrim-tr-20', 86 | '.scrim-bl-10', 87 | '.scrim-bl-20', 88 | ]) 89 | }) 90 | 91 | 92 | it('Passes default Variants', () => { 93 | const addUtilities = sinon.stub() 94 | const e = identity 95 | const plugin = Plugin() 96 | plugin({ addUtilities, e }) 97 | const [ utilities, options ] = addUtilities.getCall(0).args 98 | 99 | expect(options).to.deep.include({ 100 | variants: ['responsive', 'hover'], 101 | }) 102 | }) 103 | 104 | it('Passes custom Variants', () => { 105 | const addUtilities = sinon.stub() 106 | const e = identity 107 | const plugin = Plugin({ 108 | variants: ['myCustomVariant'], 109 | }) 110 | plugin({ addUtilities, e }) 111 | const [ utilities, options ] = addUtilities.getCall(0).args 112 | 113 | expect(options).to.deep.include({ 114 | variants: ['myCustomVariant'], 115 | }) 116 | }) 117 | }) 118 | -------------------------------------------------------------------------------- /test/spec-helper.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai') 2 | const { expect } = chai 3 | 4 | const sinon = require('sinon') 5 | const sinonChai = require('sinon-chai') 6 | 7 | chai.use(sinonChai) 8 | 9 | module.exports = { expect, sinon } 10 | --------------------------------------------------------------------------------