From 62a772a9c63b42b0df0418b9e58546673aff8e96 Mon Sep 17 00:00:00 2001 From: Martijn Cuppens Date: Mon, 31 Dec 2018 12:50:11 +0100 Subject: Move files to root to make imports easier --- postcss.js | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 postcss.js (limited to 'postcss.js') diff --git a/postcss.js b/postcss.js new file mode 100644 index 0000000..7263b93 --- /dev/null +++ b/postcss.js @@ -0,0 +1,212 @@ +// PostCSS RFS plugin +// +// Automated font-resizing +// +// See https://github.com/twbs/rfs + +'use strict'; + +const postcss = require('postcss'); + +module.exports = postcss.plugin('postcss-rfs', function (opts) { + const BREAKPOINT_ERROR = 'breakpoint option is invalid, it must be set in `px`, `rem` or `em`.'; + const BREAKPOINT_UNIT_ERROR = 'breakpointUnit option is invalid, it must be `px`, `rem` or `em`.'; + const BASE_FONT_SIZE_ERROR = 'baseFontSize option is invalid, it must be set in `px` or `rem`.'; + const DISABLE_RESPONSIVE_FONT_SIZE_SELECTOR = '.disable-responsive-font-size'; + const ENABLE_RESPONSIVE_FONT_SIZE_SELECTOR = '.enable-responsive-font-size'; + const defaultOptions = { + baseFontSize: 20, + fontSizeUnit: 'rem', + breakpoint: '75rem', + breakpointUnit: 'px', + factor: 10, + twoDimensional: false, + unitPrecision: 5, + remValue: 16, + propList: ['responsive-font-size', 'rfs'], + class: false, + safariIframeResizeBugFix: false, + enableResponsiveFontSizes: true + }; + opts = Object.assign(defaultOptions, opts); + + if (typeof opts.baseFontSize !== 'number') { + if (opts.baseFontSize.endsWith('px')) { + opts.baseFontSize = parseFloat(opts.baseFontSize); + } + else if (opts.baseFontSize.endsWith('rem')) { + opts.baseFontSize = parseFloat(opts.baseFontSize) / opts.remValue; + } + else { + console.error(BASE_FONT_SIZE_ERROR); + } + } + + if (typeof opts.breakpoint !== 'number') { + if (opts.breakpoint.endsWith('px')) { + opts.breakpoint = parseFloat(opts.breakpoint); + } + else if (opts.breakpoint.endsWith('em')) { + opts.breakpoint = parseFloat(opts.breakpoint) * opts.remValue; + } + else { + console.error(BREAKPOINT_ERROR); + } + } + + return function (css) { + + css.walkRules(function (rule) { + + if (rule.selector.includes(DISABLE_RESPONSIVE_FONT_SIZE_SELECTOR)) { + return; + } + + rule.walkDecls(function (decl) { + // Skip if property is not in propList + if (opts.propList.indexOf(decl.prop) === -1) { + return; + } + + // Set property to font-size + decl.prop = 'font-size'; + + // Skip if value is not in px or rem + if (isNaN(decl.value) && !new RegExp(/(\d*\.?\d+)(px|rem)/g).test(decl.value)) { + return; + } + + // Get the float value of the value + let value = parseFloat(decl.value); + + // Multiply by remValue if value is in rem + if (decl.value.indexOf('rem') > -1) { + value *= opts.remValue; + } + + // Render value in desired unit + if (opts.fontSizeUnit === 'px') { + decl.value = toFixed(value, opts.unitPrecision) + 'px'; + } + else if (opts.fontSizeUnit === 'rem') { + decl.value = toFixed(value / opts.remValue, opts.unitPrecision) + 'rem'; + } + else { + console.error('fontSizeUnit option is not valid, it must be `px` or `rem`.'); + } + + // Only add media query if needed + if (opts.baseFontSize >= value || opts.factor <= 1 || !opts.enableResponsiveFontSizes) { + return; + } + + // Calculate font-size and font-size difference + let baseFontSize = opts.baseFontSize + (value - opts.baseFontSize) / opts.factor; + const fontSizeDiff = value - baseFontSize; + + // Divide by remValue if needed + if (opts.fontSizeUnit === 'rem') { + baseFontSize /= opts.remValue; + } + + const viewportUnit = opts.twoDimensional ? 'vmin' : 'vw'; + + value = 'calc(' + toFixed(baseFontSize, opts.unitPrecision) + opts.fontSizeUnit + ' + ' + toFixed((fontSizeDiff * 100 / opts.breakpoint), opts.unitPrecision) + viewportUnit + ')'; + + const mediaQuery = postcss.atRule(renderMediaQuery(opts)); + let rule_selector = rule.selector; + + // Prefix with .enable-responsive-font-size class if needed + if (opts.class === 'enable') { + const selectors = rule.selector.split(','); + let ruleSelector = ''; + + for (let selector of selectors) { + ruleSelector += ENABLE_RESPONSIVE_FONT_SIZE_SELECTOR + ' ' + selector + ',\n'; + ruleSelector += selector + ENABLE_RESPONSIVE_FONT_SIZE_SELECTOR + ',\n'; + } + rule_selector = ruleSelector.slice(0, -2); + } + + + const mediaQueryRule = postcss.rule({ + selector: rule_selector, + source: rule.source + }); + mediaQueryRule.append(decl.clone({value: value})); + + // Safari iframe resize bug: https://github.com/twbs/rfs/issues/14 + if (opts.safariIframeResizeBugFix) { + mediaQueryRule.append(postcss.decl({ prop: 'min-width', value: '0vw' })); + } + + mediaQuery.append(mediaQueryRule); + rule.parent.insertAfter(rule, mediaQuery.clone()); + + // Disable classes + if (opts.class === 'disable') { + const selectors = rule.selector.split(','); + let ruleSelector = ''; + + for (let selector of selectors) { + ruleSelector += selector + ',\n'; + ruleSelector += DISABLE_RESPONSIVE_FONT_SIZE_SELECTOR + ' ' + selector + ',\n'; + ruleSelector += selector + DISABLE_RESPONSIVE_FONT_SIZE_SELECTOR + ',\n'; + } + ruleSelector = ruleSelector.slice(0, - 2); + + const dc_rule = postcss.rule({ + selector: ruleSelector, + source: rule.source + }); + + dc_rule.append(decl.clone()); + rule.parent.insertAfter(rule, dc_rule); + decl.prev() || decl.next() ? decl.remove() : decl.parent.remove(); + } + }); + }); + + }; + + function renderMediaQuery (opts) { + const mediaQuery = { + name: 'media' + }; + + switch (opts.breakpointUnit) { + case 'em': + case 'rem': + const breakpoint = opts.breakpoint / opts.remValue; + + if (opts.twoDimensional) { + mediaQuery.params = '(max-width: ' + breakpoint + opts.breakpointUnit + '), (max-height: ' + breakpoint + opts.breakpointUnit + ')'; + } + else { + mediaQuery.params = '(max-width: ' + breakpoint + opts.breakpointUnit + ')'; + } + break; + + case 'px': + if (opts.twoDimensional) { + mediaQuery.params = '(max-width: ' + opts.breakpoint + 'px), (max-height: ' + opts.breakpoint + 'px)'; + } + else { + mediaQuery.params = '(max-width: ' + opts.breakpoint + 'px)'; + } + break; + + default: + console.error(BREAKPOINT_UNIT_ERROR); + break; + } + + return mediaQuery; + } + + function toFixed (number, precision) { + const multiplier = Math.pow(10, precision + 1), + wholeNumber = Math.floor(number * multiplier); + return Math.round(wholeNumber / 10) * 10 / multiplier; + } +}); -- cgit v1.2.3