├── .gitignore ├── .prettierrc.json ├── README.md ├── dist ├── index.d.ts ├── index.d.ts.map ├── index.js └── src │ └── utils │ ├── makeStylesInline.d.ts │ ├── makeStylesInline.d.ts.map │ ├── makeStylesInline.js │ ├── makeStylesInline.test.d.ts │ ├── makeStylesInline.test.d.ts.map │ ├── makeStylesInline.test.js │ ├── rgbToHex.d.ts │ ├── rgbToHex.d.ts.map │ └── rgbToHex.js ├── eslint.config.mjs ├── index.ts ├── jest.config.js ├── package-lock.json ├── package.json ├── src ├── mocks │ └── example-template.html └── utils │ ├── makeStylesInline.test.ts │ ├── makeStylesInline.ts │ └── rgbToHex.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | .idea 4 | 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tailwind to Inline styles converter 2 | 3 | ### 🚀 Problem Solved 4 | This package addresses a common challenge in email template creation: the need for inline styles. With tailwind-to-inline, you can: 5 | 6 | - Quickly craft your templates using Tailwind CSS 7 | - Automatically convert Tailwind classes to inline styles 8 | - Generate email-ready HTML templates effortlessly 9 | 10 | No more manual inline styling - save time and reduce errors in your email template workflow! 11 | 12 | 13 | ### 🔥 Top Features 14 | 15 | - Effortless Conversion: Transform Tailwind classes to inline styles with a single function call 16 | - Dynamic Content Support: Easily replace placeholders in your templates 17 | - Time-Saving: Eliminate the need for manual inline styling in email templates 18 | 19 | 20 | ### Installation 21 | `npm install tailwind-to-inline` 22 | 23 | 24 | ### Usage 25 | 26 | ```js 27 | import { makeStylesInline } from 'tailwind-to-inline'; 28 | ... 29 | const htmlTemplate = await makeStylesInline('templates/welcome-email.html', { 30 | name: 'John', 31 | cta_text: 'Complete Profile' 32 | }; 33 | ``` 34 | 35 | 36 | ### Parameters 37 | 38 | `templatePath` 39 | Path to the template file. 40 | 41 | `placeholderValues` *(optional)* 42 | A key-value pair object to replace dynamic content in the template. 43 | 44 | 45 | ### Example 46 | #### Original template `welcome-email.html`: 47 | 48 | ```html 49 | 50 | 51 |
52 | Welcome, {{name}} 53 |
54 |
55 | 56 | {{cta_text}} 57 | 58 |
59 | 60 | 61 | ``` 62 | 63 | #### Converted result: 64 | 65 | ```html 66 | 67 | 68 |
69 | Welcome, John 70 |
71 |
72 | 73 | Complete Profile 74 | 75 |
76 | 77 | 78 | ``` 79 | 80 | ### 🤝 Contributing 81 | 82 | Contributions, issues, and feature requests are welcome! 83 | 84 | ### 📄 License 85 | 86 | This project is MIT licensed. 87 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import { makeStylesInline } from './src/utils/makeStylesInline'; 2 | export { makeStylesInline }; 3 | //# sourceMappingURL=index.d.ts.map -------------------------------------------------------------------------------- /dist/index.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAAE,CAAC"} -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.makeStylesInline = void 0; 4 | const makeStylesInline_1 = require("./src/utils/makeStylesInline"); 5 | Object.defineProperty(exports, "makeStylesInline", { enumerable: true, get: function () { return makeStylesInline_1.makeStylesInline; } }); 6 | -------------------------------------------------------------------------------- /dist/src/utils/makeStylesInline.d.ts: -------------------------------------------------------------------------------- 1 | type TMakeStylesInline = (templatePath: string, placeholderValues?: { 2 | [key: string]: string; 3 | }) => Promise; 4 | export declare const makeStylesInline: TMakeStylesInline; 5 | export {}; 6 | //# sourceMappingURL=makeStylesInline.d.ts.map -------------------------------------------------------------------------------- /dist/src/utils/makeStylesInline.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"makeStylesInline.d.ts","sourceRoot":"","sources":["../../../src/utils/makeStylesInline.ts"],"names":[],"mappings":"AASA,KAAK,iBAAiB,GAAG,CACvB,YAAY,EAAE,MAAM,EACpB,iBAAiB,CAAC,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,KAC1C,OAAO,CAAC,MAAM,CAAC,CAAC;AA6DrB,eAAO,MAAM,gBAAgB,EAAE,iBAW9B,CAAC"} -------------------------------------------------------------------------------- /dist/src/utils/makeStylesInline.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.makeStylesInline = void 0; 39 | const fs = __importStar(require("fs")); 40 | const juice_1 = __importDefault(require("juice")); 41 | const handlebars_1 = __importDefault(require("handlebars")); 42 | const postcss_1 = __importDefault(require("postcss")); 43 | const tailwindcss_1 = __importDefault(require("tailwindcss")); 44 | const autoprefixer_1 = __importDefault(require("autoprefixer")); 45 | const rgbToHex_1 = require("./rgbToHex"); 46 | const processTailwindCSS = (html) => __awaiter(void 0, void 0, void 0, function* () { 47 | const tailwindConfig = { 48 | content: [{ raw: html, extension: 'html' }], 49 | corePlugins: { 50 | preflight: false, 51 | }, 52 | }; 53 | const result = yield (0, postcss_1.default)([ 54 | (0, tailwindcss_1.default)(tailwindConfig), 55 | autoprefixer_1.default, 56 | ]).process('@tailwind components; @tailwind utilities;', { 57 | from: undefined, 58 | }); 59 | return result.css; 60 | }); 61 | const simplifyColors = (css) => { 62 | // Remove CSS variables coming from Tailwind (starting with "--tw-...") 63 | const generalSimplifications = css 64 | .replace(/rgb\(([^)]+)\) \/ var\(--tw-[^)]+\)/g, 'rgb($1)') 65 | .replace(/rgba\(([^,]+),([^,]+),([^,]+),var\(--tw-[^)]+\)\)/g, 'rgba($1,$2,$3,1)') 66 | .replace(/var\(--tw-[^)]+\)/g, '1') 67 | .replace(/--tw-[^:]+:[^;]+;/g, ''); 68 | // Since email agents like Gmail don't allow using `rgb()` colors, we replace them with their `hex` counterparts 69 | const hexColorsInsteadOfRgb = generalSimplifications.replaceAll(/(rgba?\(\d+\s+\d+\s+\d+\s*\/.*\))/g, (match) => { 70 | return (0, rgbToHex_1.rgbToHex)(match); 71 | }); 72 | return hexColorsInsteadOfRgb; 73 | }; 74 | const removeCssClasses = (css) => { 75 | return css.replaceAll(/\s*class=["'][^"']*["']/g, ''); 76 | }; 77 | const inlineStyles = (html) => __awaiter(void 0, void 0, void 0, function* () { 78 | const tailwindCss = yield processTailwindCSS(html); 79 | const simplifiedCss = simplifyColors(tailwindCss); 80 | return (0, juice_1.default)(html, { 81 | extraCss: simplifiedCss, 82 | applyStyleTags: true, 83 | removeStyleTags: true, 84 | preserveMediaQueries: true, 85 | preserveFontFaces: true, 86 | preserveImportant: true, 87 | inlinePseudoElements: true, 88 | }); 89 | }); 90 | const makeStylesInline = (templatePath, data) => __awaiter(void 0, void 0, void 0, function* () { 91 | const templateSource = fs.readFileSync(templatePath, 'utf8'); 92 | const template = handlebars_1.default.compile(templateSource); 93 | const html = template(data); 94 | const inlinedStyles = yield inlineStyles(html); 95 | return removeCssClasses(inlinedStyles); 96 | }); 97 | exports.makeStylesInline = makeStylesInline; 98 | -------------------------------------------------------------------------------- /dist/src/utils/makeStylesInline.test.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=makeStylesInline.test.d.ts.map -------------------------------------------------------------------------------- /dist/src/utils/makeStylesInline.test.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"makeStylesInline.test.d.ts","sourceRoot":"","sources":["../../../src/utils/makeStylesInline.test.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/src/utils/makeStylesInline.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const makeStylesInline_1 = require("./makeStylesInline"); 13 | describe('renderEmailFromTemplate', () => { 14 | const templatePath = 'src/mocks/example-template.html'; 15 | test('should render email from template', () => __awaiter(void 0, void 0, void 0, function* () { 16 | const placeholderValues = { 17 | name: 'John Doe', 18 | thank_you: 'Thank you for signing up!', 19 | cta_link: 'https://example.com', 20 | cta_text: 'See all features', 21 | }; 22 | const inlinedHtml = yield (0, makeStylesInline_1.makeStylesInline)(templatePath, placeholderValues); 23 | expect(inlinedHtml).toEqual(` 24 | 25 | Test title 26 | 27 | 28 |
29 | Welcome, John Doe 30 |
31 |
32 | See all features 33 |
34 | 35 | 36 | `); 37 | })); 38 | }); 39 | -------------------------------------------------------------------------------- /dist/src/utils/rgbToHex.d.ts: -------------------------------------------------------------------------------- 1 | export declare const rgbToHex: (rgb: string) => string; 2 | //# sourceMappingURL=rgbToHex.d.ts.map -------------------------------------------------------------------------------- /dist/src/utils/rgbToHex.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"rgbToHex.d.ts","sourceRoot":"","sources":["../../../src/utils/rgbToHex.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,QAAS,MAAM,WAqBnC,CAAC"} -------------------------------------------------------------------------------- /dist/src/utils/rgbToHex.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.rgbToHex = void 0; 4 | const rgbToHex = (rgb) => { 5 | // Regular expression to match RGB(A) values 6 | const rgbRegex = /rgba?\((\d+)\s*,?\s*(\d+)\s*,?\s*(\d+)\s*(?:,?\s*\/?\s*(?:\d*\.?\d+)?)?\)/i; 7 | const match = rgb.match(rgbRegex); 8 | if (!match) { 9 | throw new Error('Invalid RGB string format'); 10 | } 11 | // Extract RGB values 12 | const [, r, g, b] = match.map(Number); 13 | // Ensure values are within 0-255 range 14 | const clamp = (value) => Math.max(0, Math.min(255, value)); 15 | // Convert to hex 16 | const toHex = (value) => clamp(value).toString(16).padStart(2, '0'); 17 | return `#${toHex(r)}${toHex(g)}${toHex(b)}`; 18 | }; 19 | exports.rgbToHex = rgbToHex; 20 | // Example usage: 21 | // console.log(rgbToHex('rgba(59 130 246 / 1)')); // Output: #3b82f6 22 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import tseslint from "typescript-eslint"; 3 | 4 | export default [ 5 | { 6 | files: ["**/*.ts"], 7 | languageOptions: { 8 | globals: globals.browser, 9 | parser: tseslint.parser, 10 | parserOptions: { 11 | project: "./tsconfig.json", 12 | }, 13 | }, 14 | ignores: ["dist/**/*"], 15 | plugins: { 16 | "@typescript-eslint": tseslint.plugin, 17 | }, 18 | rules: { 19 | ...tseslint.configs.recommended.rules, 20 | "no-console": ["error"], 21 | "no-unused-vars": "off", 22 | "@typescript-eslint/no-unused-vars": ["error"] 23 | }, 24 | } 25 | ]; 26 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import { makeStylesInline } from './src/utils/makeStylesInline'; 2 | 3 | export { makeStylesInline }; 4 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const jestConfig = { 2 | preset: 'ts-jest', 3 | testMatch: ['**/?(*.)+(spec|test).ts'], 4 | }; 5 | 6 | module.exports = jestConfig; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tailwind-to-inline", 3 | "version": "1.0.5", 4 | "description": "Convert HTML templates with Tailwind CSS classes to inline styles. Ideal for email templates, making Tailwind-styled emails compatible with major email clients.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/vardan-arm/tailwind-to-inline" 8 | }, 9 | "bugs": { 10 | "url": "https://github.com/vardan-arm/tailwind-to-inline/issues" 11 | }, 12 | "homepage": "https://github.com/vardan-arm/tailwind-to-inline#readme", 13 | "main": "dist/index.js", 14 | "types": "dist/index.d.ts", 15 | "files": [ 16 | "dist/**/*" 17 | ], 18 | "scripts": { 19 | "start": "ts-node index.ts", 20 | "build": "rm -rf dist && tsc --outDir dist --declaration --declarationDir dist", 21 | "test": "jest", 22 | "lint": "eslint . --fix --config eslint.config.mjs", 23 | "format": "prettier --ignore-path .gitignore dist --write \"**/*.+(js|ts|json|html)\"" 24 | }, 25 | "keywords": [ 26 | "tailwind", 27 | "inline-styles", 28 | "email-template", 29 | "converter", 30 | "css-inliner", 31 | "tailwind-inliner", 32 | "tailwind-email", 33 | "utility-classes", 34 | "responsive-email", 35 | "dynamic-template" 36 | ], 37 | "author": "Vardan Hakobyan", 38 | "license": "ISC", 39 | "devDependencies": { 40 | "@eslint/js": "^9.9.1", 41 | "@types/jest": "^29.5.12", 42 | "eslint": "^9.9.1", 43 | "globals": "^15.9.0", 44 | "jest": "^29.7.0", 45 | "prettier": "^3.3.3", 46 | "ts-jest": "^29.2.5", 47 | "ts-node": "^10.9.2", 48 | "typescript": "^5.5.4", 49 | "typescript-eslint": "^8.4.0" 50 | }, 51 | "dependencies": { 52 | "autoprefixer": "^10.4.20", 53 | "fs": "^0.0.1-security", 54 | "handlebars": "^4.7.8", 55 | "juice": "^11.0.0", 56 | "postcss": "^8.4.43", 57 | "tailwindcss": "^3.4.10" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/mocks/example-template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Test title 4 | 5 | 6 |
7 | Welcome, {{name}} 8 |
9 |
10 | {{cta_text}} 11 |
12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /src/utils/makeStylesInline.test.ts: -------------------------------------------------------------------------------- 1 | import { makeStylesInline } from './makeStylesInline'; 2 | 3 | describe('renderEmailFromTemplate', () => { 4 | const templatePath = 'src/mocks/example-template.html'; 5 | 6 | test('should render email from template', async () => { 7 | const placeholderValues = { 8 | name: 'John Doe', 9 | thank_you: 'Thank you for signing up!', 10 | cta_link: 'https://example.com', 11 | cta_text: 'See all features', 12 | }; 13 | 14 | const inlinedHtml = await makeStylesInline(templatePath, placeholderValues); 15 | 16 | expect(inlinedHtml).toEqual(` 17 | 18 | Test title 19 | 20 | 21 |
22 | Welcome, John Doe 23 |
24 |
25 | See all features 26 |
27 |
28 | 29 | 30 | `); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/utils/makeStylesInline.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import juice from 'juice'; 3 | import Handlebars from 'handlebars'; 4 | import postcss from 'postcss'; 5 | import tailwindcss from 'tailwindcss'; 6 | import autoprefixer from 'autoprefixer'; 7 | 8 | import { rgbToHex } from './rgbToHex'; 9 | 10 | type TMakeStylesInline = ( 11 | templatePath: string, 12 | placeholderValues?: { [key: string]: string }, 13 | ) => Promise; 14 | 15 | const processTailwindCSS = async (html: string): Promise => { 16 | const tailwindConfig = { 17 | content: [{ raw: html, extension: 'html' }], 18 | corePlugins: { 19 | preflight: false, 20 | }, 21 | }; 22 | 23 | const result = await postcss([ 24 | tailwindcss(tailwindConfig), 25 | autoprefixer, 26 | ]).process('@tailwind components; @tailwind utilities;', { 27 | from: undefined, 28 | }); 29 | 30 | return result.css; 31 | }; 32 | 33 | const simplifyColors = (css: string): string => { 34 | // Remove CSS variables coming from Tailwind (starting with "--tw-...") 35 | const generalSimplifications = css 36 | .replace(/rgb\(([^)]+)\) \/ var\(--tw-[^)]+\)/g, 'rgb($1)') 37 | .replace( 38 | /rgba\(([^,]+),([^,]+),([^,]+),var\(--tw-[^)]+\)\)/g, 39 | 'rgba($1,$2,$3,1)', 40 | ) 41 | .replace(/var\(--tw-[^)]+\)/g, '1') 42 | .replace(/--tw-[^:]+:[^;]+;/g, ''); 43 | 44 | // Since email agents like Gmail don't allow using `rgb()` colors, we replace them with their `hex` counterparts 45 | const hexColorsInsteadOfRgb = generalSimplifications.replaceAll( 46 | /(rgba?\(\d+\s+\d+\s+\d+\s*\/.*\))/g, 47 | (match) => { 48 | return rgbToHex(match); 49 | }, 50 | ); 51 | 52 | return hexColorsInsteadOfRgb; 53 | }; 54 | 55 | const removeCssClasses = (css: string) => { 56 | // https://claude.ai/chat/9475aaf1-207a-4921-8b7d-f7a2b14c265f 57 | const regex = /\s*class=(['"])(?:(?!\1)[^\\]|\\.)*\1/g; 58 | 59 | return css.replaceAll(regex, ''); 60 | }; 61 | 62 | const inlineStyles = async (html: string): Promise => { 63 | const tailwindCss = await processTailwindCSS(html); 64 | const simplifiedCss = simplifyColors(tailwindCss); 65 | 66 | return juice(html, { 67 | extraCss: simplifiedCss, 68 | applyStyleTags: true, 69 | removeStyleTags: true, 70 | preserveMediaQueries: true, 71 | preserveFontFaces: true, 72 | preserveImportant: true, 73 | inlinePseudoElements: true, 74 | }); 75 | }; 76 | 77 | export const makeStylesInline: TMakeStylesInline = async ( 78 | templatePath, 79 | data, 80 | ) => { 81 | const templateSource = fs.readFileSync(templatePath, 'utf8'); 82 | const template = Handlebars.compile(templateSource); 83 | const html = template(data); 84 | 85 | const inlinedStyles = await inlineStyles(html); 86 | 87 | return removeCssClasses(inlinedStyles); 88 | }; 89 | -------------------------------------------------------------------------------- /src/utils/rgbToHex.ts: -------------------------------------------------------------------------------- 1 | export const rgbToHex = (rgb: string) => { 2 | // Regular expression to match RGB(A) values 3 | const rgbRegex = 4 | /rgba?\((\d+)\s*,?\s*(\d+)\s*,?\s*(\d+)\s*(?:,?\s*\/?\s*(?:\d*\.?\d+)?)?\)/i; 5 | 6 | const match = rgb.match(rgbRegex); 7 | 8 | if (!match) { 9 | throw new Error('Invalid RGB string format'); 10 | } 11 | 12 | // Extract RGB values 13 | const [, r, g, b] = match.map(Number); 14 | 15 | // Ensure values are within 0-255 range 16 | const clamp = (value: number) => Math.max(0, Math.min(255, value)); 17 | 18 | // Convert to hex 19 | const toHex = (value: number) => clamp(value).toString(16).padStart(2, '0'); 20 | 21 | return `#${toHex(r)}${toHex(g)}${toHex(b)}`; 22 | }; 23 | 24 | // Example usage: 25 | // console.log(rgbToHex('rgba(59 130 246 / 1)')); // Output: #3b82f6 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "outDir": "dist", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "declarationDir": "dist", 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "lib": ["DOM", "ES2021.String"] 12 | }, 13 | "include": ["**/*.ts"], 14 | "exclude": ["dist", "node_modules"] 15 | } 16 | --------------------------------------------------------------------------------