├── .npmrc ├── dist ├── src │ ├── index.d.ts │ └── generateShadows.d.ts ├── index.cjs ├── index.umd.cjs └── index.js ├── .gitignore ├── img ├── screenshot.png ├── thumb-play.png ├── play-thumbnail-text-shadow.png └── banner.svg ├── .prettierrc ├── src ├── types.d.ts ├── generateShadows.ts └── index.ts ├── CHANGELOG.md ├── .npmignore ├── release.config.cjs ├── .prettierignore ├── .github └── workflows │ ├── release-it.yaml │ ├── release.yaml │ └── test.yaml ├── tsconfig.json ├── .relase-it.json ├── vite.config.ts ├── LICENCE ├── coverage └── coverage-summary.json ├── test └── generateShadows.test.ts ├── package.json └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers=true 2 | -------------------------------------------------------------------------------- /dist/src/index.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .idea* 3 | /node_modules 4 | .turbo 5 | .npmrc -------------------------------------------------------------------------------- /dist/src/generateShadows.d.ts: -------------------------------------------------------------------------------- 1 | export default function generateShadows(steps?: number): string; 2 | -------------------------------------------------------------------------------- /img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/designbycode/tailwindcss-text-shadow/HEAD/img/screenshot.png -------------------------------------------------------------------------------- /img/thumb-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/designbycode/tailwindcss-text-shadow/HEAD/img/thumb-play.png -------------------------------------------------------------------------------- /img/play-thumbnail-text-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/designbycode/tailwindcss-text-shadow/HEAD/img/play-thumbnail-text-shadow.png -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": false, 5 | "singleQuote": false, 6 | "printWidth": 180, 7 | "useTabs": false, 8 | "singleLineLinkTags": false 9 | } 10 | -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'tailwindcss/plugin' { 2 | const plugin: any 3 | export default plugin 4 | } 5 | 6 | declare module 'tailwindcss/lib/util/flattenColorPalette' { 7 | const flattenColorPalette: any 8 | export default flattenColorPalette 9 | } -------------------------------------------------------------------------------- /src/generateShadows.ts: -------------------------------------------------------------------------------- 1 | export default function generateShadows(steps: number = 1): string { 2 | const classes: string[] = [] 3 | for (let x = 0; x < steps; x++) { 4 | classes.push(`calc(var(--ts-text-shadow-x) * ${x}) calc(var(--ts-text-shadow-y) * ${x}) var(--ts-text-shadow-blur) var(--ts-text-shadow-color)`) 5 | } 6 | return classes.toString() 7 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v1.1.1 4 | 5 | ### Added or Changed 6 | 7 | - Change default shadow color to rgb 8 | - Add video to Readme 9 | 10 | ## v1.1.0 11 | 12 | ### Added or Changed 13 | 14 | - Converted project to Typescript 15 | - Added with options feature to set default style. 16 | 17 | ## v1.0.0 18 | 19 | ### Added or Changed 20 | 21 | - Added this changelog :) 22 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .idea* 3 | /img* 4 | index.html 5 | mix-manifest.json 6 | .babelrc.json 7 | .eslintrc.json 8 | .gitignore 9 | /src* 10 | /test* 11 | .prettierrc 12 | babel.config.json 13 | gulpfile.js 14 | postcss-config.json 15 | tailwindcss.config.js 16 | tsconfig.json 17 | webpack.mix.js 18 | .turbo* 19 | esbuild.config.js 20 | 21 | *.jpeg 22 | *.jpg 23 | *.png 24 | *.svg 25 | README.md 26 | CHANGELOG.md 27 | img -------------------------------------------------------------------------------- /release.config.cjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | branches: ['main'], 3 | plugins: [ 4 | '@semantic-release/commit-analyzer', 5 | '@semantic-release/release-notes-generator', 6 | ["@semantic-release/git", { 7 | "assets": ["dist/*.js"], 8 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 9 | }], 10 | '@semantic-release/github', 11 | ] 12 | }; 13 | 14 | module.exports = config; -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | pnpm-debug.log* 6 | pnpm-error.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | coverage 10 | bower_components 11 | package.json 12 | package-lock.json 13 | node_modules/ 14 | .npm 15 | .pnpm 16 | .idea/ 17 | .DS_Store 18 | __tests__/* 19 | !__tests__/util/* 20 | examples/* 21 | !examples/**/*.config.js 22 | .run 23 | .editorconfig 24 | cache/ 25 | .github/ 26 | .eslintrc.json 27 | *.md 28 | .prettierrc -------------------------------------------------------------------------------- /.github/workflows/release-it.yaml: -------------------------------------------------------------------------------- 1 | name: Release to npm 2 | on: workflow_dispatch 3 | 4 | jobs: 5 | publish: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | - uses: actions/setup-node@v2 10 | with: 11 | node-version: '20.x' 12 | cache: 'pnpm' 13 | - uses: pnpm/action-setup@v2 14 | with: 15 | version: '7.x' 16 | - run: pnpm install 17 | - run: npm ci --no-package-lock 18 | - run: pnpm publish 19 | env: 20 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | "moduleResolution": "bundler", 9 | "allowImportingTsExtensions": true, 10 | "resolveJsonModule": true, 11 | "isolatedModules": true, 12 | "noEmit": true, 13 | "strict": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "declaration": true, 18 | "declarationDir": "./dist", 19 | "outDir": "./dist" 20 | }, 21 | "include": ["src"], 22 | "exclude": ["node_modules"] 23 | } -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | jobs: 5 | build: 6 | permissions: 7 | contents: write 8 | issues: write 9 | pull-requests: write 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | node-version: [ 20 ] 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: pnpm/action-setup@v2 17 | with: 18 | version: 7 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | cache: 'pnpm' 24 | - run: pnpm install --frozen-lockfile 25 | - run: pnpm run build 26 | - run: npx semantic-release 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | -------------------------------------------------------------------------------- /.relase-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "github": { 3 | "release": true, 4 | "releaseName": "v${version}" 5 | }, 6 | "git": { 7 | "changelog": "npx auto-changelog --stdout --commit-limit false --unreleased --template https://raw.githubusercontent.com/release-it/release-it/master/templates/changelog-compact.hbs", 8 | "commitMessage": "v${version} release", 9 | "requireCleanWorkingDir": false, 10 | "addUntrackedFiles": false, 11 | "commit": true, 12 | "push": true, 13 | "tagAnnotation": "Release v${version}", 14 | "tagName": "v${version}" 15 | }, 16 | "npm" : { 17 | "publish" : true 18 | }, 19 | "hooks": { 20 | "after:bump": "npx auto-changelog -p" 21 | }, 22 | "plugins": { 23 | "@release-it/keep-a-changelog": { 24 | "filename": "CHANGELOG.md", 25 | "strictLatest" : false 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from "path" 2 | import { defineConfig } from "vitest/config" 3 | import dts from "vite-plugin-dts" 4 | 5 | export default defineConfig({ 6 | test: { 7 | // @ts-expect-error 8 | coverage: { 9 | reporter: ["json-summary", "text"], 10 | }, 11 | }, 12 | build: { 13 | lib: { 14 | // Could also be a dictionary or array of multiple entry points 15 | entry: resolve(__dirname, "src/index.ts"), 16 | name: "Tailwindcss Text Shadow", 17 | // the proper extensions will be added 18 | fileName: "index", 19 | formats: ["es", "cjs", "umd"], 20 | }, 21 | minify: "terser", 22 | rollupOptions: { 23 | external: ["tailwindcss", "postcss"], 24 | output: { 25 | globals: { 26 | tailwindcss: "tailwindcss", 27 | postcss: "postcss" 28 | } 29 | } 30 | } 31 | }, 32 | plugins: [dts()], 33 | }) 34 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: 'Test' 2 | on: 3 | push: 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | node-version: [ 20 ] 11 | 12 | permissions: 13 | # Required to check out the code 14 | contents: read 15 | # Required to put a comment into the pull-request 16 | pull-requests: write 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - uses: pnpm/action-setup@v2 21 | with: 22 | version: 7 23 | - uses: actions/setup-node@v3 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | cache: 'pnpm' 27 | - name: 'Install Deps' 28 | run: pnpm install --frozen-lockfile 29 | - name: 'Test' 30 | run: pnpm test 31 | # - name: 'Test coverage' 32 | # run: npx vitest --coverage 33 | - name: 'Report Coverage' 34 | if: always() # Also generate the report if tests are failing 35 | uses: davelosert/vitest-coverage-report-action@v2 -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Claude Myburgh @ DesignByCode 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /coverage/coverage-summary.json: -------------------------------------------------------------------------------- 1 | {"total": {"lines":{"total":127,"covered":7,"skipped":0,"pct":5.51},"statements":{"total":127,"covered":7,"skipped":0,"pct":5.51},"functions":{"total":3,"covered":1,"skipped":0,"pct":33.33},"branches":{"total":4,"covered":2,"skipped":0,"pct":50},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}} 2 | ,"C:\\www\\tailwindcss-plugins\\packages\\tailwindcss-text-shadow\\release.config.cjs": {"lines":{"total":14,"covered":0,"skipped":0,"pct":0},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":14,"covered":0,"skipped":0,"pct":0},"branches":{"total":1,"covered":0,"skipped":0,"pct":0}} 3 | ,"C:\\www\\tailwindcss-plugins\\packages\\tailwindcss-text-shadow\\src\\generateShadows.ts": {"lines":{"total":7,"covered":7,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":7,"covered":7,"skipped":0,"pct":100},"branches":{"total":2,"covered":2,"skipped":0,"pct":100}} 4 | ,"C:\\www\\tailwindcss-plugins\\packages\\tailwindcss-text-shadow\\src\\index.ts": {"lines":{"total":106,"covered":0,"skipped":0,"pct":0},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":106,"covered":0,"skipped":0,"pct":0},"branches":{"total":1,"covered":0,"skipped":0,"pct":0}} 5 | } 6 | -------------------------------------------------------------------------------- /test/generateShadows.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from "vitest" 2 | import generateShadows from "../src/generateShadows" 3 | 4 | describe("# Generate shadow", () => { 5 | const singleLine = `calc(var(--ts-text-shadow-x) * 0) calc(var(--ts-text-shadow-y) * 0) var(--ts-text-shadow-blur) var(--ts-text-shadow-color)` 6 | 7 | const doubleLine = `calc(var(--ts-text-shadow-x) * 0) calc(var(--ts-text-shadow-y) * 0) var(--ts-text-shadow-blur) var(--ts-text-shadow-color),calc(var(--ts-text-shadow-x) * 1) calc(var(--ts-text-shadow-y) * 1) var(--ts-text-shadow-blur) var(--ts-text-shadow-color)` 8 | 9 | const tenSteps = `calc(var(--ts-text-shadow-x) * 0) calc(var(--ts-text-shadow-y) * 0) var(--ts-text-shadow-blur) var(--ts-text-shadow-color),calc(var(--ts-text-shadow-x) * 1) calc(var(--ts-text-shadow-y) * 1) var(--ts-text-shadow-blur) var(--ts-text-shadow-color),calc(var(--ts-text-shadow-x) * 2) calc(var(--ts-text-shadow-y) * 2) var(--ts-text-shadow-blur) var(--ts-text-shadow-color),calc(var(--ts-text-shadow-x) * 3) calc(var(--ts-text-shadow-y) * 3) var(--ts-text-shadow-blur) var(--ts-text-shadow-color),calc(var(--ts-text-shadow-x) * 4) calc(var(--ts-text-shadow-y) * 4) var(--ts-text-shadow-blur) var(--ts-text-shadow-color),calc(var(--ts-text-shadow-x) * 5) calc(var(--ts-text-shadow-y) * 5) var(--ts-text-shadow-blur) var(--ts-text-shadow-color),calc(var(--ts-text-shadow-x) * 6) calc(var(--ts-text-shadow-y) * 6) var(--ts-text-shadow-blur) var(--ts-text-shadow-color),calc(var(--ts-text-shadow-x) * 7) calc(var(--ts-text-shadow-y) * 7) var(--ts-text-shadow-blur) var(--ts-text-shadow-color),calc(var(--ts-text-shadow-x) * 8) calc(var(--ts-text-shadow-y) * 8) var(--ts-text-shadow-blur) var(--ts-text-shadow-color),calc(var(--ts-text-shadow-x) * 9) calc(var(--ts-text-shadow-y) * 9) var(--ts-text-shadow-blur) var(--ts-text-shadow-color)` 10 | 11 | it("should be 1 shadow line", () => { 12 | expect(generateShadows(1)).toEqual(singleLine) 13 | }) 14 | 15 | it("should be 2 shadow line", () => { 16 | expect(generateShadows(2)).toEqual(doubleLine) 17 | }) 18 | 19 | it("should be 10 shadow line", () => { 20 | expect(generateShadows(10)).toEqual(tenSteps) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /dist/index.cjs: -------------------------------------------------------------------------------- 1 | "use strict";const t=require("postcss");function e(t=1){const e=[];for(let o=0;o({})){const o=function(o){return{__options:o,handler:t(o),config:e(o)}};return o.__isOptionsFunction=!0,o.__pluginFunction=t,o.__configFunction=e,o};const o=e}(n),function(t){Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return o}});function e(t){return t&&t.__esModule?t:{default:t}}const o=e(n).default}(s);const a=o((s.__esModule?s:{default:s}).default),{default:r}=require("tailwindcss/lib/util/flattenColorPalette");module.exports=a.withOptions((function(o={}){const s=o.prefix||"text-shadow";return function({addBase:n,addComponents:a,matchUtilities:u,matchComponents:d,theme:l}){n([t.comment({text:"! tailwindcss-text-shadow v2.1.6 | MIT License | https://designbycode.co.za"})]),n({":root":{"--ts-text-shadow-color":o.shadowColor||"rgba(0, 0,0,0.45)","--ts-text-shadow-x":o.shadowOffsetX||"1px","--ts-text-shadow-y":o.shadowOffsetY||"1px","--ts-text-shadow-blur":o.shadowBlur||"2px"}}),a({[`.${s}`]:{textShadow:"var(--ts-text-shadow-x) var(--ts-text-shadow-y) var(--ts-text-shadow-blur) var(--ts-text-shadow-color)"}}),u({[`${s}-x`]:t=>({"--ts-text-shadow-x":t}),[`${s}-y`]:t=>({"--ts-text-shadow-y":t}),[`${s}-blur`]:t=>({"--ts-text-shadow-blur":t})},{values:l("textShadowSteps"),type:"length",supportsNegativeValues:!0}),u({[`${s}`]:t=>({"--ts-text-shadow-color":t})},{values:r(l("colors")),type:"color"}),a({[`.${s}-sm`]:{textShadow:e(l("textShadowSteps")[0])}}),d({[`${s}`]:t=>({textShadow:e(t)})},{type:"number",values:l("textShadowLong")})}}),(function(){return{theme:{experimental:!1,textShadowLong:{sm:4,md:8,lg:12,xl:16},textShadowSteps:{xs:"1px",sm:"2px",md:"3px",lg:"4px",xl:"5px",0:"0",1:"1px",2:"2px",3:"3px",4:"4px",5:"5px",6:"6px",7:"7px",8:"8px",9:"9px",10:"10px"}}}})); 2 | -------------------------------------------------------------------------------- /dist/index.umd.cjs: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(require("postcss")):"function"==typeof define&&define.amd?define(["postcss"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).postcss)}(this,(function(t){"use strict";function e(t=1){const e=[];for(let o=0;o({})){const o=function(o){return{__options:o,handler:t(o),config:e(o)}};return o.__isOptionsFunction=!0,o.__pluginFunction=t,o.__configFunction=e,o};const o=e}(n),function(t){Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return o}});function e(t){return t&&t.__esModule?t:{default:t}}const o=e(n).default}(s);const a=o((s.__esModule?s:{default:s}).default),{default:r}=require("tailwindcss/lib/util/flattenColorPalette");module.exports=a.withOptions((function(o={}){const s=o.prefix||"text-shadow";return function({addBase:n,addComponents:a,matchUtilities:d,matchComponents:u,theme:l}){n([t.comment({text:"! tailwindcss-text-shadow v2.1.6 | MIT License | https://designbycode.co.za"})]),n({":root":{"--ts-text-shadow-color":o.shadowColor||"rgba(0, 0,0,0.45)","--ts-text-shadow-x":o.shadowOffsetX||"1px","--ts-text-shadow-y":o.shadowOffsetY||"1px","--ts-text-shadow-blur":o.shadowBlur||"2px"}}),a({[`.${s}`]:{textShadow:"var(--ts-text-shadow-x) var(--ts-text-shadow-y) var(--ts-text-shadow-blur) var(--ts-text-shadow-color)"}}),d({[`${s}-x`]:t=>({"--ts-text-shadow-x":t}),[`${s}-y`]:t=>({"--ts-text-shadow-y":t}),[`${s}-blur`]:t=>({"--ts-text-shadow-blur":t})},{values:l("textShadowSteps"),type:"length",supportsNegativeValues:!0}),d({[`${s}`]:t=>({"--ts-text-shadow-color":t})},{values:r(l("colors")),type:"color"}),a({[`.${s}-sm`]:{textShadow:e(l("textShadowSteps")[0])}}),u({[`${s}`]:t=>({textShadow:e(t)})},{type:"number",values:l("textShadowLong")})}}),(function(){return{theme:{experimental:!1,textShadowLong:{sm:4,md:8,lg:12,xl:16},textShadowSteps:{xs:"1px",sm:"2px",md:"3px",lg:"4px",xl:"5px",0:"0",1:"1px",2:"2px",3:"3px",4:"4px",5:"5px",6:"6px",7:"7px",8:"8px",9:"9px",10:"10px"}}}}))})); 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@designbycode/tailwindcss-text-shadow", 3 | "version": "2.2.1", 4 | "description": "Tailwindcss utilities for text stroke", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/DesignByCode/tailwindcss-text-shadow.git" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/DesignByCode/tailwindcss-text-shadow/issues" 12 | }, 13 | "homepage": "https://github.com/DesignByCode/tailwindcss-text-shadow#readme", 14 | "keywords": [ 15 | "front-end", 16 | "frontend", 17 | "css", 18 | "tailwindcss", 19 | "plugin", 20 | "tailwindcss-plugin", 21 | "tailwindcss text-shadow", 22 | "text-shadow", 23 | "designbycode" 24 | ], 25 | "type": "module", 26 | "types": "dist/", 27 | "main": "./dist/index.umd.cjs", 28 | "exports": { 29 | ".": { 30 | "import": "./dist/index.js", 31 | "require": "./dist/index.umd.cjs" 32 | } 33 | }, 34 | "module": "./dist/index.js", 35 | "files": [ 36 | "dist" 37 | ], 38 | "publishConfig": { 39 | "access": "public" 40 | }, 41 | "scripts": { 42 | "build": "tsc --declaration && vite build", 43 | "clean": "rd /s /q dist .cache>nul 2>&1|echo.>nul", 44 | "dev": "pnpm build --watch", 45 | "prepublishOnly": "pnpm build", 46 | "release": "release-it", 47 | "test": "vitest --coverage" 48 | }, 49 | "lint-staged": { 50 | "src/**/*.ts": [ 51 | "prettier --write", 52 | "git add" 53 | ] 54 | }, 55 | "authors": [ 56 | { 57 | "name": "Claude Myburgh", 58 | "email": "claude@designbycode.co.za", 59 | "url": "https://designbycode.co.za" 60 | } 61 | ], 62 | "browserslist": [ 63 | "> .5% or last 2 versions" 64 | ], 65 | "licenses": [ 66 | { 67 | "type": "MIT", 68 | "url": "https://github.com/DesignByCode/tailwindcss-text-stroke/blob/main/LICENCE" 69 | } 70 | ], 71 | "peerDependencies": { 72 | "tailwindcss": ">=3.0.0 || >=3.0.0-alpha.1" 73 | }, 74 | "devDependencies": { 75 | "@release-it/bumper": "^6.0.1", 76 | "@release-it/conventional-changelog": "^8.0.1", 77 | "@release-it/keep-a-changelog": "^5.0.0", 78 | "@semantic-release/git": "^10.0.1", 79 | "@semantic-release/github": "^10.3.3", 80 | "@types/node": "^22.5.4", 81 | "@vitest/coverage-v8": "^2.0.5", 82 | "autoprefixer": "^10.4.20", 83 | "husky": "^9.1.5", 84 | "postcss": "^8.4.45", 85 | "prettier": "^3.3.3", 86 | "release-it": "^17.6.0", 87 | "semantic-release": "^24.1.0", 88 | "tailwindcss": "^3.4.10", 89 | "terser": "^5.32.0", 90 | "typescript": "^5.6.2", 91 | "vite": "^5.4.3", 92 | "vite-plugin-dts": "^4.2.1", 93 | "vitest": "^2.0.5" 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { comment } from "postcss" 2 | import generateShadows from "./generateShadows" 3 | 4 | import { version } from "../package.json" 5 | 6 | import plugin from "tailwindcss/plugin" 7 | 8 | const { default: flattenColorPalette } = require("tailwindcss/lib/util/flattenColorPalette") 9 | 10 | interface StepProps { 11 | [key: string]: string 12 | } 13 | 14 | interface OptionsProps { 15 | experimental?: boolean 16 | shadowColor?: string 17 | shadowOffsetX?: string 18 | shadowOffsetY?: string 19 | shadowBlur?: string 20 | prefix?: string 21 | } 22 | 23 | module.exports = plugin.withOptions( 24 | function (options: OptionsProps = {}) { 25 | const prefix = options.prefix || "text-shadow" 26 | 27 | return function ({ addBase, addComponents, matchUtilities, matchComponents, theme }: any): void { 28 | addBase([ 29 | comment({ 30 | text: `! tailwindcss-text-shadow v${version} | MIT License | https://designbycode.co.za`, 31 | }), 32 | ]) 33 | 34 | addBase({ 35 | ":root": { 36 | "--ts-text-shadow-color": options.shadowColor || "rgba(0, 0,0,0.45)", 37 | "--ts-text-shadow-x": options.shadowOffsetX || "1px", 38 | "--ts-text-shadow-y": options.shadowOffsetY || "1px", 39 | "--ts-text-shadow-blur": options.shadowBlur || "2px", 40 | }, 41 | }) 42 | 43 | addComponents({ 44 | [`.${prefix}`]: { 45 | textShadow: `var(--ts-text-shadow-x) var(--ts-text-shadow-y) var(--ts-text-shadow-blur) var(--ts-text-shadow-color)`, 46 | }, 47 | }) 48 | 49 | matchUtilities( 50 | { 51 | [`${prefix}-x`]: (value: StepProps) => ({ 52 | "--ts-text-shadow-x": value, 53 | }), 54 | [`${prefix}-y`]: (value: StepProps) => ({ 55 | "--ts-text-shadow-y": value, 56 | }), 57 | [`${prefix}-blur`]: (value: StepProps) => ({ 58 | "--ts-text-shadow-blur": value, 59 | }), 60 | }, 61 | { 62 | values: theme("textShadowSteps"), 63 | type: "length", 64 | supportsNegativeValues: true, 65 | } 66 | ) 67 | 68 | matchUtilities( 69 | { 70 | [`${prefix}`]: (value: StepProps) => ({ 71 | "--ts-text-shadow-color": value, 72 | }), 73 | }, 74 | { 75 | values: flattenColorPalette(theme("colors")), 76 | type: "color", 77 | } 78 | ) 79 | 80 | addComponents({ 81 | [`.${prefix}-sm`]: { 82 | textShadow: generateShadows(theme("textShadowSteps")[0]), 83 | }, 84 | }) 85 | 86 | matchComponents( 87 | { 88 | [`${prefix}`]: (value: number) => ({ 89 | textShadow: generateShadows(value), 90 | }), 91 | }, 92 | { 93 | type: "number", 94 | values: theme("textShadowLong"), 95 | } 96 | ) 97 | } 98 | }, 99 | function () { 100 | return { 101 | theme: { 102 | experimental: false, 103 | textShadowLong: { 104 | sm: 4, 105 | md: 8, 106 | lg: 12, 107 | xl: 16, 108 | }, 109 | textShadowSteps: { 110 | xs: "1px", 111 | sm: "2px", 112 | md: "3px", 113 | lg: "4px", 114 | xl: "5px", 115 | 0: "0", 116 | 1: "1px", 117 | 2: "2px", 118 | 3: "3px", 119 | 4: "4px", 120 | 5: "5px", 121 | 6: "6px", 122 | 7: "7px", 123 | 8: "8px", 124 | 9: "9px", 125 | 10: "10px", 126 | }, 127 | }, 128 | } 129 | } 130 | ) 131 | -------------------------------------------------------------------------------- /img/banner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 101 |
102 |

TailwindCSS Text Shadow Plugin 103 |

104 |
105 | 🎉 106 | MADE FOR 107 | 🎉 108 |
109 |

Tailwindcss

110 |
111 |
112 |
113 |
-------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | var __getOwnPropNames = Object.getOwnPropertyNames; 2 | var __commonJS = (cb, mod) => function __require() { 3 | return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; 4 | }; 5 | import { comment } from "postcss"; 6 | var require_index = __commonJS({ 7 | "index.js"(exports, module) { 8 | function generateShadows(steps = 1) { 9 | const classes = []; 10 | for (let x = 0; x < steps; x++) { 11 | classes.push(`calc(var(--ts-text-shadow-x) * ${x}) calc(var(--ts-text-shadow-y) * ${x}) var(--ts-text-shadow-blur) var(--ts-text-shadow-color)`); 12 | } 13 | return classes.toString(); 14 | } 15 | const version = "2.1.6"; 16 | function getDefaultExportFromCjs(x) { 17 | return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x; 18 | } 19 | var createPlugin$2 = {}; 20 | var createPlugin$1 = {}; 21 | (function(exports2) { 22 | Object.defineProperty(exports2, "__esModule", { 23 | value: true 24 | }); 25 | Object.defineProperty(exports2, "default", { 26 | enumerable: true, 27 | get: function() { 28 | return _default; 29 | } 30 | }); 31 | function createPlugin2(plugin2, config) { 32 | return { 33 | handler: plugin2, 34 | config 35 | }; 36 | } 37 | createPlugin2.withOptions = function(pluginFunction, configFunction = () => ({})) { 38 | const optionsFunction = function(options) { 39 | return { 40 | __options: options, 41 | handler: pluginFunction(options), 42 | config: configFunction(options) 43 | }; 44 | }; 45 | optionsFunction.__isOptionsFunction = true; 46 | optionsFunction.__pluginFunction = pluginFunction; 47 | optionsFunction.__configFunction = configFunction; 48 | return optionsFunction; 49 | }; 50 | const _default = createPlugin2; 51 | })(createPlugin$1); 52 | (function(exports2) { 53 | Object.defineProperty(exports2, "__esModule", { 54 | value: true 55 | }); 56 | Object.defineProperty(exports2, "default", { 57 | enumerable: true, 58 | get: function() { 59 | return _default; 60 | } 61 | }); 62 | const _createPlugin = /* @__PURE__ */ _interop_require_default(createPlugin$1); 63 | function _interop_require_default(obj) { 64 | return obj && obj.__esModule ? obj : { 65 | default: obj 66 | }; 67 | } 68 | const _default = _createPlugin.default; 69 | })(createPlugin$2); 70 | let createPlugin = createPlugin$2; 71 | var plugin = (createPlugin.__esModule ? createPlugin : { default: createPlugin }).default; 72 | const plugin$1 = /* @__PURE__ */ getDefaultExportFromCjs(plugin); 73 | const { default: flattenColorPalette } = require("tailwindcss/lib/util/flattenColorPalette"); 74 | module.exports = plugin$1.withOptions( 75 | function(options = {}) { 76 | const prefix = options.prefix || "text-shadow"; 77 | return function({ addBase, addComponents, matchUtilities, matchComponents, theme }) { 78 | addBase([ 79 | comment({ 80 | text: `! tailwindcss-text-shadow v${version} | MIT License | https://designbycode.co.za` 81 | }) 82 | ]); 83 | addBase({ 84 | ":root": { 85 | "--ts-text-shadow-color": options.shadowColor || "rgba(0, 0,0,0.45)", 86 | "--ts-text-shadow-x": options.shadowOffsetX || "1px", 87 | "--ts-text-shadow-y": options.shadowOffsetY || "1px", 88 | "--ts-text-shadow-blur": options.shadowBlur || "2px" 89 | } 90 | }); 91 | addComponents({ 92 | [`.${prefix}`]: { 93 | textShadow: `var(--ts-text-shadow-x) var(--ts-text-shadow-y) var(--ts-text-shadow-blur) var(--ts-text-shadow-color)` 94 | } 95 | }); 96 | matchUtilities( 97 | { 98 | [`${prefix}-x`]: (value) => ({ 99 | "--ts-text-shadow-x": value 100 | }), 101 | [`${prefix}-y`]: (value) => ({ 102 | "--ts-text-shadow-y": value 103 | }), 104 | [`${prefix}-blur`]: (value) => ({ 105 | "--ts-text-shadow-blur": value 106 | }) 107 | }, 108 | { 109 | values: theme("textShadowSteps"), 110 | type: "length", 111 | supportsNegativeValues: true 112 | } 113 | ); 114 | matchUtilities( 115 | { 116 | [`${prefix}`]: (value) => ({ 117 | "--ts-text-shadow-color": value 118 | }) 119 | }, 120 | { 121 | values: flattenColorPalette(theme("colors")), 122 | type: "color" 123 | } 124 | ); 125 | addComponents({ 126 | [`.${prefix}-sm`]: { 127 | textShadow: generateShadows(theme("textShadowSteps")[0]) 128 | } 129 | }); 130 | matchComponents( 131 | { 132 | [`${prefix}`]: (value) => ({ 133 | textShadow: generateShadows(value) 134 | }) 135 | }, 136 | { 137 | type: "number", 138 | values: theme("textShadowLong") 139 | } 140 | ); 141 | }; 142 | }, 143 | function() { 144 | return { 145 | theme: { 146 | experimental: false, 147 | textShadowLong: { 148 | sm: 4, 149 | md: 8, 150 | lg: 12, 151 | xl: 16 152 | }, 153 | textShadowSteps: { 154 | xs: "1px", 155 | sm: "2px", 156 | md: "3px", 157 | lg: "4px", 158 | xl: "5px", 159 | 0: "0", 160 | 1: "1px", 161 | 2: "2px", 162 | 3: "3px", 163 | 4: "4px", 164 | 5: "5px", 165 | 6: "6px", 166 | 7: "7px", 167 | 8: "8px", 168 | 9: "9px", 169 | 10: "10px" 170 | } 171 | } 172 | }; 173 | } 174 | ); 175 | } 176 | }); 177 | export default require_index(); 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Title banner 3 | 4 | 5 | ## Tailwind CSS Text Shadow Plugin 6 | 7 | [![NPM](https://nodei.co/npm/@designbycode/tailwindcss-text-shadow.png?mini=true)](https://nodei.co/npm/@designbycode/tailwindcss-text-shadow/) 8 | 9 | [![npm version](https://badge.fury.io/js/@designbycode%2Ftailwindcss-text-shadow.svg)](https://badge.fury.io/js/@designbycode%2Ftailwindcss-text-shadow) 10 | ![npm](https://img.shields.io/npm/dt/%40designbycode/tailwindcss-text-shadow) 11 | ![NPM](https://img.shields.io/npm/l/%40designbycode%2Ftailwindcss-text-shadow) 12 | ![npm bundle size](https://img.shields.io/bundlephobia/min/%40designbycode%2Ftailwindcss-text-shadow) 13 | [![Test](https://github.com/DesignByCode/tailwindcss-text-shadow/actions/workflows/test.yaml/badge.svg)](https://github.com/DesignByCode/tailwindcss-text-shadow/actions/workflows/test.yaml) 14 | ![ts](https://badgen.net/badge/Built%20With/TypeScript/blue) 15 | [![GitHub stars](https://img.shields.io/github/stars/DesignByCode/tailwindcss-text-shadow?style=social)](https://github.com/DesignByCode/tailwindcss-text-shadow/stargazers) 16 | 17 | 18 | The Tailwind CSS **Text Shadow Plugin** extends the default set of utility classes in **Tailwind CSS** to provide easy text shadow customization for your web projects. With this plugin, you can apply custom text shadows using utility 19 | classes or define your own text shadow variations based on predefined steps and color palettes. 20 | 21 | ![TextShadow Plugin](img/screenshot.png) 22 | 23 | ## 📇 Table of Contents 24 | 25 | - [Installation](#installation) 26 | - [Using pnpm](#using-pnpm) 27 | - [Using npm](#using-npm) 28 | - [Using yarn](#using-yarn) 29 | - [Setup](#setup) 30 | - [Default configuration styles](#default-configuration-styles) 31 | - [Use](#use) 32 | - [Video Tutorial](#video-tutorial) 33 | - [Apply Text Shadows](#apply-text-shadows) 34 | - [Text shadow blur modifier](#text-shadow-blur-modifier) 35 | - [Text shadow x and y modifiers](#text-shadow-x-and-y-modifiers) 36 | - [Text shadow color modifier](#text-shadow-color-modifier) 37 | - [Text shadow color opacity](#text-shadow-color-opacity) 38 | - [Long shadow](#long-text-shadow) 39 | - [Customization](#customization) 40 | - [Example](#example) 41 | - [Contributing](#contributing) 42 | - [License](#license) 43 | - [Author](#author) 44 | - [Acknowledgments](#acknowledgments) 45 | 46 | ## Installation 47 | 48 | To use this plugin, you need to have Tailwind CSS installed in your project. If you haven't installed Tailwind CSS yet, follow these steps: 49 | 50 | #### Using pnpm 51 | 52 | ```bash 53 | pnpm add @designbycode/tailwindcss-text-shadow 54 | ``` 55 | 56 | #### Using npm 57 | 58 | ```bash 59 | npm install @designbycode/tailwindcss-text-shadow 60 | ``` 61 | 62 | #### Using yarn 63 | 64 | ```bash 65 | yarn add @designbycode/tailwindcss-text-shadow 66 | ``` 67 | 68 | ### Setup 69 | 70 | 1. Add the Plugin to your Tailwind CSS Config 71 | In your tailwind.config.js file, add the plugin to the plugins array: 72 | 73 | ```javascript 74 | module.exports = { 75 | // ...other configurations 76 | plugins: [ 77 | // ...other plugins 78 | require("@designbycode/tailwindcss-text-shadow"), 79 | ], 80 | } 81 | ``` 82 | 83 | ### Default configuration styles 84 | 85 | 2. If the default styles do not suit your preferences, you can effortlessly customize them using the following configuration options 86 | 87 | ```javascript 88 | module.exports = { 89 | // ...other configurations 90 | require("@designbycode/tailwindcss-text-shadow" 91 | ) 92 | ({ 93 | shadowColor: "rgba(0, 0, 0, 0.5)", 94 | shadowBlur: "3px", 95 | shadowOffsetX: "2px", 96 | shadowOffsetY: "2px", 97 | }) 98 | ``` 99 | 100 | ## Use 101 | 102 | ### Video Tutorial 103 | 104 | See plugin in action in video below. 👇 105 | 106 | [![Tailwindcss text-shadow tutorial](img/thumb-play.png)](https://youtu.be/Xb0wAMAGAHE) 107 | 108 | ### Apply Text Shadows 109 | 110 | Once the plugin is added to your Tailwind CSS configuration, you can use the provided utility classes to apply text shadows to your HTML elements. 111 | 112 | ```html 113 |

Hello, Tailwind CSS!

114 | ``` 115 | 116 | ### Text shadow blur modifier 117 | 118 | To make the spread or blur bigger add the `.text-shadow-blur-{value}` 119 | 120 | ```html 121 |

Hello, Tailwind CSS!

122 | ``` 123 | 124 | ### Text shadow x and y modifiers 125 | 126 | The shadow can be moved on the xy axis using `.text-shadow-x-{value}` and `.text-shadow-y-{value}` 127 | 128 | ```html 129 |

Hello, Tailwind CSS!

130 | ``` 131 | 132 | ### Text shadow color modifier 133 | 134 | ```html 135 |

Hello, Tailwind CSS!

136 | ``` 137 | 138 | In the example above, the `

` element will have a red text shadow with an x offset of 3px, a y offset of 4px, and a blur radius of 2px. The text-shadow class enables the text shadow styles, while the text-shadow-x-md, 139 | text-shadow-y-lg, and 140 | text-shadow-blur-2 classes customize the horizontal offset, vertical offset, and blur radius, respectively. 141 | 142 | ### Text shadow color opacity 143 | 144 | To change the opacity of the text-shadow-color use the following method 145 | 146 | ```html 147 |

Hello, Tailwind CSS!

148 | 149 | 150 | 151 |

Hello, Tailwind CSS!

152 | 153 | 154 | 155 |

Hello, Tailwind CSS!

156 | ``` 157 | 158 | > **Warning** 159 | > New experimental long shadow feature 160 | 161 | ```javascript 162 | module.exports = { 163 | // ...other configurations 164 | require("@designbycode/tailwindcss-text-shadow" 165 | ) 166 | ({ 167 | experimental: true, // 👈 168 | }) 169 | ``` 170 | 171 | > Note the latest version doesn't need experimental anymore 172 | 173 | ### Long text shadow 174 | 175 | The long shadow is a new experimental feature that I add. It creates shadow that stacks to any amount. The classes is `.text-shadow-sm` or `.text-shadow-[steps]` 176 | 177 | ```html 178 |

Hello, Tailwind CSS!

179 | 180 |

Hello, Tailwind CSS!

181 | ``` 182 | 183 | [![Tailwindcss text-shadow tutorial](img/play-thumbnail-text-shadow.png)](https://youtu.be/quyJpz5dtcs) 184 | 185 | ## Customization 186 | 187 | You can customize the available text shadow options by modifying the theme.textShadowSteps property in your tailwind.config.js file. The steps defined in this object will be used to generate utility classes for each aspect of the text 188 | shadow. 189 | 190 | ### Example 191 | 192 | ```javascript 193 | // tailwind.config.js 194 | module.exports = { 195 | theme: { 196 | prefix: 'text-shadow', 197 | textShadowSteps: { 198 | sm: "1px", 199 | md: "2px", 200 | lg: "3px", 201 | xl: "4px", 202 | 0: "0", 203 | 1: "1px", 204 | 2: "2px", 205 | 3: "3px", 206 | 4: "4px", 207 | }, 208 | }, 209 | } 210 | ``` 211 | 212 | In this example, we have customized the textShadowSteps object with only four steps for sm, md, lg, and xl, and removed the rest. The plugin will generate utility classes accordingly. 213 | 214 | ### Change class name prefix 215 | If you don't like to use class name `.text-shadow` or are getting conflicts in application you can change it to whatever you want like `.textShadow`, `ts` or whatever you want. Change it in theme settings. 216 | 217 | ```typescript 218 | // tailwind.config.js 219 | module.exports = { 220 | theme: { 221 | prefix: 'ts', 222 | }, 223 | } 224 | ``` 225 | 226 | ### Use 227 | ```html 228 |
229 | ``` 230 | 231 | ## Contributing 232 | 233 | Contributions to this plugin are welcome! If you encounter any issues, have feature requests, or want to improve the plugin, feel free to create a pull request or submit an issue on the GitHub repository. 234 | 235 | ### Contributors 236 | 237 | 238 | 239 | 240 | 241 | ## License 242 | 243 | This project is licensed under the [MIT](LICENCE) License - see the [LICENSE](LICENCE) file for details. 244 | 245 | ## Author 246 | 247 |
248 | Claude Myburgh 249 |
250 |

Claude Myburgh

252 | 253 | ## Acknowledgments 254 | 255 | - This plugin is inspired by the needs of web developers using Tailwind CSS. 256 | - Special thanks to the Tailwind CSS team for creating such an amazing framework. 257 | --------------------------------------------------------------------------------