tailwindcss-px-to-responsive
Version:
A Tailwind plugin for converting pixel values to rem, vw, vh units. It helps you to create Design-to-Development workflow easier and faster.
107 lines (102 loc) • 3.18 kB
text/typescript
/** @type {import('tailwindcss').Config} */
import plugin from "tailwindcss/plugin";
import { spacing, possibleUtilities } from "./utilities";
// px 값을 vw, vh, rem 등으로 변환
function numberToUnit(value: string | number, unit: string): string {
const cleanValue = (typeof value === "string" ? value : `${value}`).trim();
const remDivider = 16;
const number = parseFloat(cleanValue);
if (!number) return value as string;
if (unit === "vw") return number === 0 ? "0" : `calc(${number} * var(--vw))`;
if (unit === "vh") return number === 0 ? "0" : `calc(${number} * var(--vh))`;
if (unit === "rem") return number === 0 ? "0" : `${number / remDivider}rem`;
return value as string;
}
// 1~max까지의 값 객체 생성
function makeNumberValues(max = 100, unit = ""): Record<string, string> {
const values: Record<string, string> = {};
const remDivider = 16;
for (let i = 1; i <= max; i++) {
if (unit === "rem") {
values[i] = `${i / remDivider}rem`;
values[`-${i}`] = `-${i / remDivider}rem`;
} else {
values[i] = `${i}`;
values[`-${i}`] = `-${i}`;
}
}
return values;
}
// 유틸리티별 플러그인 생성
function createUtilityPlugin(
utilityMap: Map<string, string | string[]>,
util: string,
unit: string,
max: number
) {
// utilityNames: 배열(예: ["h"])
return plugin(function ({ matchUtilities }: { matchUtilities: any }) {
const cssProp = utilityMap.get(util);
matchUtilities(
{
[util]: (value: string | number) => {
if (Array.isArray(cssProp)) {
const style: Record<string, string> = {};
for (const prop of cssProp) {
style[prop] = numberToUnit(value, unit);
}
return style;
} else {
return { [cssProp as string]: numberToUnit(value, unit) };
}
},
},
{ values: makeNumberValues(max, unit) }
);
});
}
// 메인 preset 생성 함수
export default (
options: {
utilities?: string[];
max?: number;
unit?: string;
theme?: string;
}[]
) => {
const plugins: any[] | undefined = [];
const themeExtend: Record<string, any> = {};
for (const option of options) {
if (option.utilities) {
for (const [utilityMap, themeKey] of possibleUtilities) {
for (const util of option.utilities) {
if (utilityMap.has(util)) {
// 플러그인: 해당 유틸리티만
plugins.push(
createUtilityPlugin(
utilityMap,
util,
option.unit || "",
option.max || 100
)
);
}
}
}
}
if (option.theme) {
let next;
if (spacing.has(option.theme)) {
next = makeNumberValues(option.max);
} else {
next = makeNumberValues(option.max || 100, option.unit || "");
}
themeExtend[option.theme] = next;
}
}
// var(--vh, --vw) 기본값 생성
return {
theme: { extend: themeExtend },
plugins,
};
};