tailwindcss-golden-ratio
Version:
A Tailwind CSS plugin to add spacing utility classes based on the Golden Ratio
324 lines (277 loc) • 10.4 kB
JavaScript
const plugin = require('tailwindcss/plugin');
const gr = 1.61803398875; // The Golden Ratio
function getGolden(theme) {
const prefix = theme('goldenRatio.prefix', true);
const grPrefix = prefix ? (prefix === true ? 'gr-' : prefix) : '';
const grPercentPrefix = grPrefix ? grPrefix.replace(/-$/, 'p-') : '';
const grFixedPrefix = grPrefix ? grPrefix.replace(/-$/, 'f-') : '';
const spacerUnit = theme('goldenRatio.spacerUnit', 'rem');
const spacerBase = parseFloat(theme('goldenRatio.spacerBase', 1.5));
const fixedSpacerUnit = theme('goldenRatio.fixedSpacerUnit', 'px');
const fixedUnitsPerSpacerUnit = theme('goldenRatio.fixedUnitsPerSpacerUnit', 16);
const roundFixedValues = theme('goldenRatio.roundFixedValues', true);
const useCssVars = theme('goldenRatio.useCssVars', true);
const base1 = spacerBase / gr / gr / gr / gr / gr;
const base2 = spacerBase / gr / gr / gr / gr;
const base3 = spacerBase / gr / gr / gr;
const base4 = spacerBase / gr / gr;
const base5 = spacerBase / gr;
const base6 = spacerBase;
const base7 = spacerBase * gr;
const base8 = spacerBase * gr * gr;
const base9 = spacerBase * gr * gr * gr;
const base10 = spacerBase * gr * gr * gr * gr;
const base11 = spacerBase * gr * gr * gr * gr * gr;
const base = {
gr1: `${base1}${spacerUnit}`, // 0.135269rem
gr2: `${base2}${spacerUnit}`, // 0.218865rem
gr3: `${base3}${spacerUnit}`, // 0.354124rem
gr4: `${base4}${spacerUnit}`, // 0.572973rem
gr5: `${base5}${spacerUnit}`, // 0.92707rem
gr6: `${base6}${spacerUnit}`, // 1.5rem
gr7: `${base7}${spacerUnit}`, // 2.427rem
gr8: `${base8}${spacerUnit}`, // 3.92689rem
gr9: `${base9}${spacerUnit}`, // 6.35371rem
gr10: `${base10}${spacerUnit}`, // 10.2803rem
gr11: `${base11}${spacerUnit}`, // 16.6335rem
};
// Generate half and double values
for (let i = 1; i <= 11; i++) {
const val = parseFloat(base[`gr${i}`]);
base[`grHalf${i}`] = `${val / 2}${spacerUnit}`;
base[`grDouble${i}`] = `${val * 2}${spacerUnit}`;
}
// Create fixed values
const fixedBase = Object.keys(base).reduce((acc, key) => {
let val = parseFloat(base[key]) * fixedUnitsPerSpacerUnit;
if (roundFixedValues) val = Math.round(val);
acc[key] = `${val}${fixedSpacerUnit}`;
return acc;
}, {});
// Create percent values
const percentBase = {
gr1: `${100 / gr / gr / gr / gr / gr}%`, // 0.0901792
gr2: `${100 / gr / gr / gr / gr}%`, // 0.14591
gr3: `${100 / gr / gr / gr}%`, // 0.236083
gr4: `${100 / gr / gr}%`, // 0.381982
gr5: `${100 / gr}%`, // 0.618047
gr6: `${100}%`, // 1
gr7: `${100 * gr}%`, // 1.61803398875
gr8: `${100 * gr * gr}%`, // 2.61798
gr9: `${100 * gr * gr * gr}%`, // 4.23589
gr10: `${100 * gr * gr * gr * gr}%`, // 6.85367
gr11: `${100 * gr * gr * gr * gr * gr}%`, // 11.0892
};
// Generate half and double values
for (let i = 1; i <= 11; i++) {
const val = parseFloat(percentBase[`gr${i}`]);
percentBase[`grHalf${i}`] = `${val / 2}%`;
percentBase[`grDouble${i}`] = `${val * 2}%`;
}
let golden = {
config: {
prefix: grPrefix,
spacerUnit,
fixedSpacerUnit,
fixedUnitsPerSpacerUnit,
roundFixedValues,
spacerBase,
useCssVars,
},
base,
fixedBase,
percentBase,
spacing: {},
scale: {},
};
// Generate classes
for (let i = 1; i <= 11; i++) {
// Dynamic spacing
golden.spacing[`${grPrefix}${i}`] = useCssVars ? `var(--golden-ratio-${i})` : base[`gr${i}`];
golden.spacing[`${grPrefix}half-${i}`] = useCssVars ? `var(--golden-ratio-half-${i})` : base[`grHalf${i}`];
golden.spacing[`${grPrefix}double-${i}`] = useCssVars ? `var(--golden-ratio-double-${i})` : base[`grDouble${i}`];
// Fixed spacing
golden.spacing[`${grFixedPrefix}${i}`] = useCssVars ? `var(--golden-ratio-fixed-${i})` : fixedBase[`gr${i}`];
golden.spacing[`${grFixedPrefix}half-${i}`] = useCssVars ? `var(--golden-ratio-fixed-half-${i})` : fixedBase[`grHalf${i}`];
golden.spacing[`${grFixedPrefix}double-${i}`] = useCssVars ? `var(--golden-ratio-fixed-double-${i})` : fixedBase[`grDouble${i}`];
// Percent scale
golden.scale[`${grPercentPrefix}${i}`] = percentBase[`gr${i}`];
golden.scale[`${grPercentPrefix}half-${i}`] = percentBase[`grHalf${i}`];
golden.scale[`${grPercentPrefix}double-${i}`] = percentBase[`grDouble${i}`];
}
return golden;
}
module.exports = plugin(
function ({ addComponents, theme }) {
const golden = getGolden(theme);
if (golden.config.useCssVars) {
let rootVars = {
'--gr': `${gr}`,
'--golden-ratio': gr,
'--golden-ratio-base': `${golden.config.spacerBase}`,
'--golden-ratio-unit': `1${golden.config.spacerUnit}`,
'--golden-ratio-fixed-unit': `1${golden.config.fixedSpacerUnit}`,
'--golden-ratio-fixed-units-per-unit': `${golden.config.fixedUnitsPerSpacerUnit}`,
};
for (let i = 1; i <= 11; i++) {
// Dynamic spacing
rootVars[`--golden-ratio-${i}`] = golden.base[`gr${i}`];
rootVars[`--golden-ratio-half-${i}`] = golden.base[`grHalf${i}`];
rootVars[`--golden-ratio-double-${i}`] = golden.base[`grDouble${i}`];
// Fixed spacing
rootVars[`--golden-ratio-fixed-${i}`] = golden.fixedBase[`gr${i}`];
rootVars[`--golden-ratio-fixed-half-${i}`] = golden.fixedBase[`grHalf${i}`];
rootVars[`--golden-ratio-fixed-double-${i}`] = golden.fixedBase[`grDouble${i}`];
}
addComponents({
':root': rootVars,
});
}
},
{
theme: {
extend: {
spacing: (theme) => {
const golden = getGolden(theme);
return {
...golden.spacing,
...golden.scale,
};
},
aspectRatio: (theme) => {
const golden = getGolden(theme);
const aspectBase = {
gr1: `${1 / gr / gr / gr / gr / gr} / 1`,
gr2: `${1 / gr / gr / gr / gr} / 1`,
gr3: `${1 / gr / gr / gr} / 1`,
gr4: `${1 / gr / gr} / 1`,
gr5: `${1 / gr} / 1`,
gr6: '1 / 1',
gr7: `${1 * gr} / 1`,
gr8: `${1 * gr * gr} / 1`,
gr9: `${1 * gr * gr * gr} / 1`,
gr10: `${1 * gr * gr * gr * gr} / 1`,
gr11: `${1 * gr * gr * gr * gr * gr} / 1`,
grHalf1: `${0.5 / gr / gr / gr / gr / gr} / 1`,
grHalf2: `${0.5 / gr / gr / gr / gr} / 1`,
grHalf3: `${0.5 / gr / gr / gr} / 1`,
grHalf4: `${0.5 / gr / gr} / 1`,
grHalf5: `${0.5 / gr} / 1`,
grHalf6: '0.5 / 1',
grHalf7: `${0.5 * gr} / 1`,
grHalf8: `${0.5 * gr * gr} / 1`,
grHalf9: `${0.5 * gr * gr * gr} / 1`,
grHalf10: `${0.5 * gr * gr * gr * gr} / 1`,
grHalf11: `${0.5 * gr * gr * gr * gr * gr} / 1`,
grDouble1: `${2 / gr / gr / gr / gr / gr} / 1`,
grDouble2: `${2 / gr / gr / gr / gr} / 1`,
grDouble3: `${2 / gr / gr / gr} / 1`,
grDouble4: `${2 / gr / gr} / 1`,
grDouble5: `${2 / gr} / 1`,
grDouble6: '2 / 1',
grDouble7: `${2 * gr} / 1`,
grDouble8: `${2 * gr * gr} / 1`,
grDouble9: `${2 * gr * gr * gr} / 1`,
grDouble10: `${2 * gr * gr * gr * gr} / 1`,
grDouble11: `${2 * gr * gr * gr * gr * gr} / 1`,
};
let aspectRatio = {};
for (let i = 1; i <= 11; i++) {
aspectRatio[`${golden.config.prefix}${i}`] = aspectBase[`gr${i}`];
aspectRatio[`${golden.config.prefix}half-${i}`] = aspectBase[`grHalf${i}`];
aspectRatio[`${golden.config.prefix}double-${i}`] = aspectBase[`grDouble${i}`];
}
return aspectRatio;
},
// backgroundSize: (theme) => ({
// ...theme('spacing'),
// }),
// borderSpacing: (theme) => ({
// ...theme('spacing'),
// }),
// borderRadius: (theme) => ({
// ...theme('spacing'),
// }),
// borderWidth: (theme) => ({
// ...theme('spacing'),
// }),
// divideWidth: (theme) => ({
// ...theme('spacing'),
// }),
// fontSize: (theme) => ({
// ...theme('spacing'),
// }),
// gap: (theme) => ({
// ...theme('spacing'),
// }),
// height: (theme) => ({
// ...theme('spacing'),
// }),
// inset: (theme) => ({
// ...theme('spacing'),
// }),
lineHeight: (theme) => ({
...theme('spacing'),
}),
// margin: (theme) => ({
// ...theme('spacing'),
// }),
// maxHeight: (theme) => ({
// ...theme('spacing'),
// }),
// maxWidth: (theme) => ({
// ...theme('spacing'),
// }),
// minHeight: (theme) => ({
// ...theme('spacing'),
// }),
// minWidth: (theme) => ({
// ...theme('spacing'),
// }),
// outlineOffset: (theme) => ({
// ...theme('spacing'),
// }),
// outlineWidth: (theme) => ({
// ...theme('spacing'),
// }),
// padding: (theme) => ({
// ...theme('spacing'),
// }),
// ringOffsetWidth: (theme) => ({
// ...theme('spacing'),
// }),
// ringWidth: (theme) => ({
// ...theme('spacing'),
// }),
scale: (theme) => {
const golden = getGolden(theme);
return golden.scale;
},
// scrollMargin: (theme) => ({
// ...theme('spacing'),
// }),
// scrollPadding: (theme) => ({
// ...theme('spacing'),
// }),
// space: (theme) => ({
// ...theme('spacing'),
// }),
// strokeWidth: (theme) => ({
// ...theme('spacing'),
// }),
// translate: (theme) => ({
// ...theme('spacing'),
// }),
// textIndent: (theme) => ({
// ...theme('spacing'),
// }),
// textUnderlineOffset: (theme) => ({
// ...theme('spacing'),
// }),
// width: (theme) => ({
// ...theme('spacing'),
// }),
},
},
},
);