Comparing version 10.0.0-alpha.0 to 10.0.0
110
lib/rfs.js
@@ -0,20 +1,23 @@ | ||
'use strict'; | ||
const postcss = require('postcss'); | ||
const valueParser = require('postcss-value-parser'); | ||
const BREAKPOINT_ERROR = 'breakpoint option is invalid, it should be set in `px`, `rem` or `em`.'; | ||
const BASE_RFS_ERROR = 'baseValue option is invalid, it should be set in `px` or `rem`.'; | ||
const defaultOptions = { | ||
baseValue: 20, | ||
unit: 'rem', | ||
breakpoint: 1200, | ||
breakpointUnit: 'px', | ||
factor: 10, | ||
twoDimensional: false, | ||
unitPrecision: 5, | ||
remValue: 16, | ||
functionName: 'rfs', | ||
enableRfs: true, | ||
mode: 'min-media-query' | ||
}; | ||
module.exports = class { | ||
constructor(opts) { | ||
this.opts = { | ||
baseValue: 20, | ||
unit: 'rem', | ||
breakpoint: 1200, | ||
factor: 10, | ||
twoDimensional: false, | ||
unitPrecision: 5, | ||
remValue: 16, | ||
functionName: 'rfs', | ||
enableRfs: true, | ||
...opts | ||
}; | ||
this.opts = { ...defaultOptions, ...opts }; | ||
@@ -25,5 +28,5 @@ if (typeof this.opts.baseValue !== 'number') { | ||
} else if (this.opts.baseValue.endsWith('rem')) { | ||
this.opts.baseValue = Number.parseFloat(this.opts.baseValue) / this.opts.remValue; | ||
this.opts.baseValue = Number.parseFloat(this.opts.baseValue) * this.opts.remValue; | ||
} else { | ||
console.error(BASE_RFS_ERROR); | ||
throw new TypeError('`baseValue` option is invalid, it should be set in `px` or `rem`.'); | ||
} | ||
@@ -38,5 +41,9 @@ } | ||
} else { | ||
console.error(BREAKPOINT_ERROR); | ||
throw new TypeError('`breakpoint` option is invalid, it should be set in `px`, `rem` or `em`.'); | ||
} | ||
} | ||
if (!['px', 'rem', 'em'].includes(this.opts.breakpointUnit)) { | ||
throw new TypeError('`breakpointUnit` option is invalid, it should be `px`, `rem` or `em`.'); | ||
} | ||
} | ||
@@ -54,14 +61,12 @@ | ||
if (value === 0) { | ||
return 0; | ||
return value; | ||
} | ||
// Render value in desired unit | ||
if (this.opts.unit === 'rem') { | ||
return `${this.toFixed(value / this.opts.remValue, this.opts.unitPrecision)}rem`; | ||
} | ||
return `${this.toFixed(value, this.opts.unitPrecision)}px`; | ||
return this.opts.unit === 'rem' ? | ||
`${this.toFixed(value / this.opts.remValue, this.opts.unitPrecision)}rem` : | ||
`${this.toFixed(value, this.opts.unitPrecision)}px`; | ||
} | ||
value(declarationValue) { | ||
process(declarationValue, fluid) { | ||
const parsed = valueParser(declarationValue); | ||
@@ -73,3 +78,3 @@ | ||
// Since we only want to transform rfs() values, | ||
// we can ignore anything else. | ||
// we can ignore everything else. | ||
if (node.type !== 'function' && node.value !== this.opts.functionName) { | ||
@@ -79,6 +84,13 @@ return; | ||
node.nodes.filter(node => node.type === 'word').forEach(node => { | ||
node.value = node.value.replace(/^(-?\d*\.?\d+)(px|rem)/g, (match, value, unit) => { | ||
const wordNodes = node.nodes.filter(node => node.type === 'word'); | ||
for (const node of wordNodes) { | ||
node.value = node.value.replace(/^(-?\d*\.?\d+)(.*)/g, (match, value, unit) => { | ||
value = Number.parseFloat(value); | ||
// Return value if it's not a number or px/rem value | ||
if (Number.isNaN(value) || !['px', 'rem'].includes(unit)) { | ||
return match; | ||
} | ||
// Convert to px if in rem | ||
@@ -90,3 +102,3 @@ if (unit === 'rem') { | ||
// Only add responsive function if needed | ||
if (this.opts.baseValue >= Math.abs(value) || this.opts.factor <= 1 || !this.opts.enableRfs) { | ||
if (!fluid || this.opts.baseValue >= Math.abs(value) || this.opts.factor <= 1 || !this.opts.enableRfs) { | ||
return this.renderValue(value); | ||
@@ -105,12 +117,11 @@ } | ||
const viewportUnit = this.opts.twoDimensional ? 'vmin' : 'vw'; | ||
const sign = value > 0 ? '+' : '-'; | ||
baseValue = this.toFixed(value > 0 ? baseValue : -baseValue, this.opts.unitPrecision); | ||
value = this.renderValue(value); | ||
const fluidValue = this.toFixed(diff * 100 / this.opts.breakpoint, this.opts.unitPrecision); | ||
return `min(${value}, calc(${baseValue}${this.opts.unit} ${sign} ${fluidValue}${viewportUnit}))`; | ||
return value > 0 ? | ||
`calc(${this.toFixed(baseValue, this.opts.unitPrecision)}${this.opts.unit} + ${this.toFixed(diff * 100 / this.opts.breakpoint, this.opts.unitPrecision)}${viewportUnit})` : | ||
`calc(-${this.toFixed(baseValue, this.opts.unitPrecision)}${this.opts.unit} - ${this.toFixed(diff * 100 / this.opts.breakpoint, this.opts.unitPrecision)}${viewportUnit})`; | ||
}); | ||
}); | ||
} | ||
// Now we will transform the existing rgba() function node | ||
// into a word node with the hex value | ||
node.type = 'word'; | ||
@@ -123,2 +134,30 @@ node.value = valueParser.stringify(node.nodes); | ||
// Return the value without `rfs()` function | ||
// eg. `4px rfs(32px)` => `.25rem 2rem` | ||
value(value) { | ||
return this.process(value, false); | ||
} | ||
// Convert `rfs()` function to fluid css | ||
// eg. `4px rfs(32px)` => `.25rem calc(1.325rem + 0.9vw)` | ||
fluidValue(value) { | ||
return this.process(value, true); | ||
} | ||
renderMediaQuery() { | ||
const mediaQuery = { | ||
name: 'media' | ||
}; | ||
const dimPrefix = this.opts.mode === 'min-media-query' ? 'min' : 'max'; | ||
const dimConnector = this.opts.mode === 'min-media-query' ? ' and' : ','; | ||
const breakpoint = this.opts.breakpointUnit === 'px' ? this.opts.breakpoint : this.opts.breakpoint / this.opts.remValue; | ||
mediaQuery.params = this.opts.twoDimensional ? | ||
`(${dimPrefix}-width: ${breakpoint}${this.opts.breakpointUnit})${dimConnector} (${dimPrefix}-height: ${breakpoint}${this.opts.breakpointUnit})` : | ||
`(${dimPrefix}-width: ${breakpoint}${this.opts.breakpointUnit})`; | ||
return postcss.atRule(mediaQuery); | ||
} | ||
getOptions() { | ||
@@ -128,2 +167,1 @@ return this.opts; | ||
}; | ||
{ | ||
"name": "rfs", | ||
"version": "10.0.0-alpha.0", | ||
"version": "10.0.0", | ||
"main": "postcss.js", | ||
@@ -30,2 +30,3 @@ "description": "Powerful & easy-to-use responsive resizing engine.", | ||
"typography", | ||
"mixin", | ||
"scss", | ||
@@ -38,13 +39,13 @@ "sass", | ||
"engines": { | ||
"node": ">=10" | ||
"node": ">=12" | ||
}, | ||
"scripts": { | ||
"mocha": "mocha", | ||
"stylelint": "stylelint \"**/*.{less,scss}\" --cache --cache-location node_modules/.cache/stylelint", | ||
"stylelint": "stylelint \"**/*.{less,scss}\" --cache --cache-location node_modules/.cache/stylelint --rd", | ||
"xo": "xo", | ||
"lint": "npm-run-all --parallel xo stylelint", | ||
"lint": "npm-run-all --parallel --aggregate-output --continue-on-error xo stylelint", | ||
"test": "npm run lint && npm run mocha", | ||
"generate-test-results": "node-sass test/sass/ -o test/expected/ && prettier --write test/expected/*.css", | ||
"examples": "npm-run-all --parallel --continue-on-error gulp-examples node-examples", | ||
"gulp-examples": "npm-run-all --parallel --continue-on-error gulp-example-*", | ||
"examples": "npm-run-all --parallel --aggregate-output --continue-on-error gulp-examples node-examples", | ||
"gulp-examples": "npm-run-all --parallel --aggregate-output --continue-on-error gulp-example-*", | ||
"gulp-example-less": "gulp --gulpfile=examples/less/gulpfile.js", | ||
@@ -54,3 +55,3 @@ "gulp-example-postcss": "gulp --gulpfile=examples/postcss/gulpfile.js", | ||
"gulp-example-stylus": "gulp --gulpfile=examples/stylus/gulpfile.js", | ||
"node-examples": "npm-run-all --parallel --continue-on-error node-example-*", | ||
"node-examples": "npm-run-all --parallel --aggregate-output --continue-on-error node-example-*", | ||
"node-example-less": "node examples/less/index.js", | ||
@@ -62,34 +63,43 @@ "node-example-postcss": "node examples/postcss/index.js", | ||
"dependencies": { | ||
"postcss-value-parser": "^4.1.0" | ||
"postcss-value-parser": "^4.2.0" | ||
}, | ||
"devDependencies": { | ||
"gulp": "^4.0.2", | ||
"gulp-less": "^4.0.1", | ||
"gulp-postcss": "^8.0.0", | ||
"gulp-sass": "^4.1.0", | ||
"gulp-stylus": "^2.7.0", | ||
"less": "^3.11.3", | ||
"mocha": "^8.0.1", | ||
"node-sass": "^4.14.1", | ||
"gulp-less": "^5.0.0", | ||
"gulp-postcss": "^9.0.1", | ||
"gulp-sass": "^5.1.0", | ||
"gulp-stylus": "^3.0.0", | ||
"less": "^4.1.3", | ||
"less3": "npm:less@^3.13.1", | ||
"mocha": "^9.2.2", | ||
"node-sass": "^8.0.0", | ||
"npm-run-all": "^4.1.5", | ||
"postcss": "^7.0.32", | ||
"prettier": "^2.0.5", | ||
"sass": "^1.26.8", | ||
"stylelint": "^13.6.1", | ||
"stylelint-config-standard": "^20.0.0", | ||
"stylus": "^0.54.7", | ||
"xo": "^0.32.0" | ||
"postcss": "^8.4.21", | ||
"postcss-less": "^6.0.0", | ||
"prettier": "^2.8.5", | ||
"sass": "^1.59.3", | ||
"stylelint": "^14.16.1", | ||
"stylelint-config-twbs-bootstrap": "^7.0.0", | ||
"stylus": "^0.59.0", | ||
"xo": "^0.52.4" | ||
}, | ||
"mocha": { | ||
"check-leaks": true, | ||
"throw-deprecation": true, | ||
"trace-deprecation": true, | ||
"trace-warnings": true, | ||
"use_strict": true | ||
}, | ||
"xo": { | ||
"space": true, | ||
"rules": { | ||
"ava/no-import-test-files": "off", | ||
"promise/prefer-await-to-then": "off", | ||
"arrow-body-style": "off", | ||
"comma-dangle": [ | ||
"error", | ||
"never" | ||
], | ||
"n/prefer-promises/fs": "off", | ||
"object-curly-spacing": [ | ||
"error", | ||
"always" | ||
], | ||
"operator-linebreak": [ | ||
"error", | ||
"after" | ||
], | ||
"prefer-template": "error", | ||
"unicorn/prefer-module": "off", | ||
"unicorn/prevent-abbreviations": "off" | ||
@@ -96,0 +106,0 @@ } |
144
postcss.js
/*! | ||
* PostCSS RFS plugin 10.0.0-alpha.0 | ||
* PostCSS RFS plugin | ||
* | ||
* Automated responsive values for font sizes, paddings, margins and much more | ||
* | ||
* Licensed under MIT (https://github.com/twbs/rfs/blob/master/LICENSE) | ||
* Licensed under MIT (https://github.com/twbs/rfs/blob/main/LICENSE) | ||
*/ | ||
@@ -14,17 +14,141 @@ | ||
module.exports = postcss.plugin('postcss-rfs', opts => { | ||
const DISABLE_RFS_SELECTOR = '.disable-rfs'; | ||
const ENABLE_RFS_SELECTOR = '.enable-rfs'; | ||
module.exports = (opts = {}) => { | ||
const rfs = new RfsClass(opts); | ||
// Get the options merged with defaults | ||
// Get the merged options | ||
opts = rfs.getOptions(); | ||
const mediaQuery = rfs.renderMediaQuery(); | ||
return css => { | ||
css.walkDecls(decl => { | ||
// Check if the selector doesn't contain the disabled selector | ||
// Check if value contains rfs() function | ||
if (new RegExp(opts.functionName + '(.*)', 'g').test(decl.value)) { | ||
decl.value = rfs.value(decl.value); | ||
css.walkRules(rule => { | ||
const mediaQueryRules = []; | ||
const extraBlocks = []; | ||
const { parent } = rule; | ||
let removeRule = false; | ||
let dcRule; | ||
let ecRule; | ||
let ruleSelector = rule.selector; | ||
// Prepare rule to add to media query | ||
if (opts.class === 'enable') { | ||
const selectors = rule.selector.split(','); | ||
let tempRuleSelector = ''; | ||
for (const selector of selectors) { | ||
tempRuleSelector += `${ENABLE_RFS_SELECTOR} ${selector},\n`; | ||
tempRuleSelector += `${selector + ENABLE_RFS_SELECTOR},\n`; | ||
} | ||
ruleSelector = tempRuleSelector.slice(0, -2); | ||
} | ||
const fluidRule = postcss.rule({ | ||
selector: ruleSelector | ||
}); | ||
// Disable classes | ||
if (opts.class === 'disable') { | ||
const selectors = rule.selector.split(','); | ||
let ruleSelector = ''; | ||
for (const selector of selectors) { | ||
ruleSelector += opts.mode === 'max-media-query' ? `${selector},\n` : ''; | ||
ruleSelector += `${DISABLE_RFS_SELECTOR} ${selector},\n`; | ||
ruleSelector += `${selector + DISABLE_RFS_SELECTOR},\n`; | ||
} | ||
ruleSelector = ruleSelector.slice(0, -2); | ||
dcRule = postcss.rule({ | ||
selector: ruleSelector, | ||
source: rule.source | ||
}); | ||
} | ||
rule.walkDecls(decl => { | ||
// Check if the selector doesn't contain the disabled selector | ||
// and if the value contains the rfs() function | ||
const check = !rule.selector.includes(DISABLE_RFS_SELECTOR) && | ||
new RegExp(`${opts.functionName}(.*)`, 'g').test(decl.value); | ||
if (!check) { | ||
return; | ||
} | ||
const value = rfs.value(decl.value); | ||
const fluidValue = rfs.fluidValue(decl.value); | ||
decl.value = value; | ||
if (value !== fluidValue) { | ||
const defaultValue = opts.mode === 'min-media-query' ? (opts.class === 'enable' ? value : fluidValue) : value; | ||
const mediaQueryValue = opts.mode === 'min-media-query' ? value : fluidValue; | ||
decl.value = defaultValue; | ||
fluidRule.append(decl.clone({ value: mediaQueryValue })); | ||
mediaQueryRules.push(fluidRule); | ||
// Disable classes | ||
if (opts.class === 'disable') { | ||
const declOpts = opts.mode === 'max-media-query' ? {} : { value }; | ||
dcRule.append(decl.clone(declOpts)); | ||
extraBlocks.push(dcRule); | ||
} else if (opts.class === 'enable' && opts.mode === 'min-media-query') { | ||
if (ecRule === undefined) { | ||
ecRule = postcss.rule({ | ||
selector: ruleSelector, | ||
source: parent.source | ||
}); | ||
} | ||
ecRule.append(decl.clone({ value: fluidValue })); | ||
extraBlocks.push(ecRule); | ||
} | ||
// Remove declaration if needed | ||
if (opts.class === 'disable' && opts.mode === 'max-media-query') { | ||
if (decl.prev() || decl.next()) { | ||
decl.remove(); | ||
} else { | ||
removeRule = true; | ||
} | ||
} | ||
} | ||
}); | ||
if (mediaQueryRules.length === 0) { | ||
return; | ||
} | ||
// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14 | ||
if (opts.safariIframeResizeBugFix) { | ||
rule.append({ | ||
prop: 'min-width', | ||
value: '0vw' | ||
}); | ||
} | ||
const fluidMediaQuery = mediaQuery.clone(); | ||
for (const mediaQueryRule of mediaQueryRules) { | ||
fluidMediaQuery.append(mediaQueryRule); | ||
} | ||
parent.insertAfter(rule, fluidMediaQuery); | ||
if (extraBlocks.length > 0) { | ||
for (const disableBlock of extraBlocks) { | ||
parent.insertAfter(rule, disableBlock); | ||
} | ||
} | ||
if (removeRule) { | ||
rule.remove(); | ||
} | ||
}); | ||
}; | ||
}); | ||
}; | ||
module.exports.postcss = true; |
510
README.md
<p align="center"> | ||
<img src="https://raw.githubusercontent.com/twbs/rfs/master/rfs.svg?sanitize=true" alt="RFS logo" width="99" height="108.6"> | ||
<img src="https://raw.githubusercontent.com/twbs/rfs/main/rfs.svg?sanitize=true" alt="RFS logo" width="99" height="108.6"> | ||
</p> | ||
@@ -14,4 +14,5 @@ | ||
[![build][build-image]][build-url] | ||
[![devDeps][devDeps-image]][devDeps-url] | ||
- [Demos](#demos) | ||
- [Advantages](#advantages) | ||
- [Installation](#installation) | ||
@@ -21,7 +22,27 @@ - [Usage](#usage) | ||
- [Configuration](#configuration) | ||
- [Demos](#demos) | ||
- [Versions](#versions) | ||
- [Creator](#creator) | ||
- [Copyright and license](#copyright-and-license) | ||
## Demos | ||
- [Card example (Sass)](https://codepen.io/MartijnCuppens/pen/vqaEBG?editors=0100) | ||
- [Card example (Sass, with custom properties)](https://codepen.io/MartijnCuppens/pen/voXLGL?editors=1100) | ||
- [Card example (PostCSS)](https://codepen.io/MartijnCuppens/pen/aeojgG?editors=0100) | ||
- [Card example (PostCSS, with custom properties)](https://codepen.io/MartijnCuppens/pen/JgRYaw?editors=0100) | ||
- [Simple font rescaling Codepen Demo](https://codepen.io/MartijnCuppens/pen/ZBjdMy?editors=0100) | ||
- [RFS in Bootstrap demo](https://project-rfs.github.io/) | ||
## Advantages | ||
- No need to rescale paddings or margins anymore. | ||
- Text won't be chopped off in smaller viewports when RFS is applied to font sizes. | ||
- RFS will prevent the font size from rescaling too small, so readability can be assured. | ||
- The font sizes of all text elements will always remain in relation with each other. | ||
## Fluid rescaling in action | ||
The following example shows the effect of RFS on font sizes: | ||
![RFS](https://raw.githubusercontent.com/twbs/rfs/main/.github/rfs-rescale.gif) | ||
## Installation | ||
@@ -33,2 +54,3 @@ | ||
- yarn: `yarn add rfs` | ||
- bower (deprecated): `bower install rfs --save` | ||
@@ -42,104 +64,415 @@ **Copy/paste (not recommended):** | ||
### Input | ||
### Sass (<code>.scss</code> syntax) | ||
#### Sass (<code>.scss</code> syntax) | ||
```text | ||
project/ | ||
├── node_modules/ | ||
│ └── rfs | ||
│ └── ... | ||
└── scss/ | ||
└── main.scss | ||
``` | ||
#### Input | ||
```scss | ||
@import "node_modules/rfs/scss"; | ||
// scss/main.scss | ||
:root { | ||
--spacer-lg: rfs(2.5rem); | ||
@import "../node_modules/rfs/scss"; | ||
.title { | ||
@include font-size(4rem); | ||
// The font-size mixin is a shorthand which calls | ||
// @include rfs(4rem, font-size); | ||
// Other shorthand mixins that are available are: | ||
// @include padding(4rem); | ||
// @include padding-top(4rem); | ||
// @include padding-right(4rem); | ||
// @include padding-bottom(4rem); | ||
// @include padding-left(4rem); | ||
// @include margin(4rem); | ||
// @include margin-top(4rem); | ||
// @include margin-right(4rem); | ||
// @include margin-bottom(4rem); | ||
// @include margin-left(4rem); | ||
// For properties which do not have a shorthand, the property can be passed: | ||
// @include rfs(4rem, border-radius); | ||
// Whenever a value contains a comma, it should be escaped with `#{}`: | ||
// @include rfs(0 0 4rem red #{","} 0 0 5rem blue, box-shadow); | ||
// Custom properties (css variables): | ||
// @include rfs(4rem, --border-radius); | ||
} | ||
``` | ||
If you're using Webpack, you can simplify the `@import` using the `~` prefix: | ||
```scss | ||
@import "~rfs/scss"; | ||
``` | ||
#### Generated css | ||
```css | ||
.title { | ||
margin-bottom: var(--spacer-lg); | ||
padding-top: rfs(.5rem) !important; | ||
font-size: rfs(4rem); | ||
font-size: calc(1.525rem + 3.3vw); | ||
} | ||
@media (min-width: 1200px) { | ||
.title { | ||
font-size: 4rem; | ||
} | ||
} | ||
``` | ||
#### Sass (<code>.sass</code> syntax) | ||
#### !important usage | ||
##### Input | ||
```scss | ||
.label { | ||
@include font-size(2.5rem !important); | ||
} | ||
``` | ||
##### Output | ||
```css | ||
.label { | ||
font-size: calc(1.375rem + 1.5vw) !important; | ||
} | ||
@media (min-width: 1200px) { | ||
.label { | ||
font-size: 2.5rem !important; | ||
} | ||
} | ||
``` | ||
### Sass (`.sass` syntax) | ||
```text | ||
project/ | ||
├── node_modules/ | ||
│ └── rfs | ||
│ └── ... | ||
└── sass/ | ||
└── main.sass | ||
``` | ||
#### Input | ||
```sass | ||
@import "node_modules/rfs/sass" | ||
// sass/main.sass | ||
:root | ||
--spacer-lg: rfs(2.5rem) | ||
@import "../node_modules/rfs/sass" | ||
.title | ||
margin-bottom: var(--spacer-lg) | ||
padding-top: rfs(.5rem) !important | ||
font-size: rfs(4rem) | ||
+font-size(4rem) | ||
// The font-size mixin is a shorthand which calls | ||
// +rfs(4rem, font-size) | ||
// Other shorthand mixins that are available are: | ||
// +padding(4rem) | ||
// +padding-top(4rem) | ||
// +padding-right(4rem) | ||
// +padding-bottom(4rem) | ||
// +padding-left(4rem) | ||
// +margin(4rem) | ||
// +margin-top(4rem) | ||
// +margin-right(4rem) | ||
// +margin-bottom(4rem) | ||
// +margin-left(4rem) | ||
// For properties which do not have a shorthand, the property can be passed: | ||
// +rfs(4rem, border-radius) | ||
// Whenever a value contains a comma, it should be escaped with `#{}`: | ||
// +rfs(0 0 4rem red #{","} 0 0 5rem blue, box-shadow) | ||
// Custom properties (css variables): | ||
// +rfs(4rem, --border-radius) | ||
``` | ||
#### PostCSS | ||
If you're using Webpack, you can simplify the `@import` using the `~` prefix: | ||
Have a look at the [examples folder](https://github.com/twbs/rfs/tree/master/examples/postcss) to find examples on how your PostCSS setup can be configured. | ||
```sass | ||
@import "~rfs/scss" | ||
``` | ||
#### Generated css | ||
```css | ||
:root { | ||
--spacer-lg: rfs(2.5rem); | ||
.title { | ||
font-size: calc(1.525rem + 3.3vw); | ||
} | ||
@media (min-width: 1200px) { | ||
.title { | ||
font-size: 4rem; | ||
} | ||
} | ||
``` | ||
#### !important usage | ||
##### Input | ||
```sass | ||
.label | ||
+font-size(2.5rem !important) | ||
``` | ||
#### output | ||
```css | ||
.label { | ||
font-size: calc(1.375rem + 1.5vw) !important; | ||
} | ||
@media (min-width: 1200px) { | ||
.label { | ||
font-size: 2.5rem !important; | ||
} | ||
} | ||
``` | ||
### PostCSS | ||
```text | ||
project/ | ||
├── postcss/ | ||
│ └── main.css | ||
└── node_modules/ | ||
└── rfs | ||
└── ... | ||
``` | ||
Have a look at the [examples folder](https://github.com/twbs/rfs/tree/main/examples/postcss) to find examples on how your PostCSS setup can be configured. | ||
```postcss | ||
// postcss/main.css | ||
.title { | ||
margin-bottom: var(--spacer-lg); | ||
padding-top: rfs(.5rem) !important; | ||
font-size: rfs(4rem); | ||
// Or use it with any other property, for example | ||
// padding: rfs(4rem); | ||
// It's also possible to pass multiple values | ||
// padding: rfs(3rem 4rem); | ||
// or even | ||
// box-shadow: rfs(0 3px 4rem red); | ||
// or even comma seperated values | ||
// box-shadow: rfs(0 3px 4rem red, 3px 0 4rem blue); | ||
// To combine it with !important, use | ||
// box-shadow: rfs(0 3px 4rem red) !important; | ||
// Custom properties (css variables): | ||
// --border-radius: rfs(4rem); | ||
} | ||
``` | ||
#### Less | ||
#### Generated css | ||
Using [unnamed lookups](http://lesscss.org/features/#mixins-feature-unnamed-lookups). | ||
```css | ||
.title { | ||
font-size: calc(1.525rem + 3.3vw); | ||
} | ||
@media (min-width: 1200px) { | ||
.title { | ||
font-size: 4rem; | ||
} | ||
} | ||
``` | ||
### Less | ||
```text | ||
project/ | ||
├── less/ | ||
│ └── main.less | ||
└── node_modules/ | ||
└── rfs | ||
└── ... | ||
``` | ||
```less | ||
@import "node_modules/rfs/less"; | ||
// less/main.less | ||
:root { | ||
--spacer-lg: .rfs(2.5rem)[]; | ||
} | ||
@import "../node_modules/rfs/less"; | ||
.title { | ||
margin-bottom: var(--spacer-lg); | ||
padding-top: rfs(.5rem)[] !important; | ||
font-size: rfs(4rem)[]; | ||
.font-size(4rem); | ||
// The font-size mixin is a shorthand which calls | ||
// .rfs(4rem, font-size); | ||
// Other shorthand mixins that are available are: | ||
// .padding(4rem); | ||
// .padding-top(4rem); | ||
// .padding-right(4rem); | ||
// 'padding-bottom(4rem); | ||
// .padding-left(4rem); | ||
// .margin(4rem); | ||
// .margin-top(4rem); | ||
// .margin-right(4rem); | ||
// .margin-bottom(4rem); | ||
// .margin-left(4rem); | ||
// For properties which do not have a shorthand, the property can be passed as: | ||
// .rfs(4rem, border-radius); | ||
// Whenever a value contains a comma, it should be escaped with a tilde(~): | ||
// .rfs(0 0 4rem red ~"," 0 0 5rem blue, box-shadow) | ||
// Custom properties (css variables): | ||
// .rfs(4rem, --border-radius) | ||
} | ||
``` | ||
#### Stylus | ||
If you're using Webpack, you can simplify the `@import` using the `~` prefix: | ||
```stylus | ||
@import "node_modules/rfs/stylus"; | ||
```less | ||
@import "~rfs/less" | ||
``` | ||
:root { | ||
--spacer-lg: rfs(2.5rem); | ||
} | ||
#### Generated css | ||
```css | ||
.title { | ||
margin-bottom: var(--spacer-lg); | ||
padding-top: rfs(.5rem) !important; | ||
font-size: rfs(4rem); | ||
font-size: calc(1.525rem + 3.3vw); | ||
} | ||
@media (min-width: 1200px) { | ||
.title { | ||
font-size: 4rem; | ||
} | ||
} | ||
``` | ||
### Generated css | ||
#### !important usage | ||
Less still has [a bug](https://github.com/less/less.js/issues/2917) for [native `!important` support](http://lesscss.org/features/#mixins-feature-the-important-keyword), and `!important` can not be accepted as a parameter, so you 'll need to pass `important` as a flag: | ||
##### Input | ||
```less | ||
.label { | ||
.font-size(2.5rem important); | ||
} | ||
``` | ||
#### output | ||
```css | ||
:root { | ||
--spacer-lg: min(2.5rem, calc(1.375rem + 1.5vw)); | ||
.label { | ||
font-size: calc(1.375rem + 1.5vw) !important; | ||
} | ||
@media (min-width: 1200px) { | ||
.label { | ||
font-size: 2.5rem !important; | ||
} | ||
} | ||
``` | ||
### Stylus | ||
```text | ||
project/ | ||
├── node_modules/ | ||
│ └── rfs | ||
│ └── ... | ||
└── stylus/ | ||
└── main.styl | ||
``` | ||
```stylus | ||
// stylus/main.styl | ||
@import "../node_modules/rfs/stylus"; | ||
.title | ||
rfs-font-size(64px) | ||
// The font-size mixin is a shorthand which calls | ||
// rfs(4rem, font-size) | ||
// Other shorthand mixins that are available are: | ||
// rfs-padding(4rem) | ||
// rfs-padding-top(4rem) | ||
// rfs-padding-right(4rem) | ||
// rfs-padding-bottom(4rem) | ||
// rfs-padding-left(4rem) | ||
// rfs-margin(4rem) | ||
// rfs-margin-top(4rem) | ||
// rfs-margin-right(4rem) | ||
// rfs-margin-bottom(4rem) | ||
// rfs-margin-left(4rem) | ||
// For properties which do not have a shorthand, the property can be passed as: | ||
// rfs(4rem, border-radius) | ||
// Whenever a value contains a comma, it should be escaped with a backslash: | ||
// rfs(0 0 4rem red \, 0 0 5rem blue, box-shadow) | ||
// Custom properties (css variables): | ||
// rfs(4rem, --border-radius) | ||
``` | ||
Note the `font-size` mixin can not be used to set the font size. That is because a `font-size()` mixin would override the `font-size` property. See [129#issuecomment-477926416](https://github.com/twbs/rfs/pull/129#issuecomment-477926416) for more info. | ||
If you're using Webpack, you can simplify the `@import` using the `~` prefix: | ||
```stylus | ||
@import "~rfs/stylus" | ||
``` | ||
#### Generated css | ||
```css | ||
.title { | ||
margin-bottom: var(--spacer-lg); | ||
padding-top: .5rem !important; | ||
font-size: min(4rem, calc(1.525rem + 3.3vw)); | ||
font-size: calc(1.525rem + 3.3vw); | ||
} | ||
@media (min-width: 1200px) { | ||
.title { | ||
font-size: 4rem; | ||
} | ||
} | ||
``` | ||
## Fluid rescaling in action | ||
#### !important usage | ||
The following example shows the effect of RFS on `padding`s, `box-shadow`s & `font-size`s: | ||
##### Input | ||
![RFS](https://raw.githubusercontent.com/twbs/rfs/master/.github/rfs-rescale.gif) | ||
```less | ||
.label | ||
rfs-font-size(2.5rem important) | ||
``` | ||
#### output | ||
```css | ||
.label { | ||
font-size: calc(1.375rem + 1.5vw) !important; | ||
} | ||
@media (min-width: 1200px) { | ||
.label { | ||
font-size: 2.5rem !important; | ||
} | ||
} | ||
``` | ||
## Visualisation | ||
@@ -149,3 +482,3 @@ | ||
![RFS visualisation](https://raw.githubusercontent.com/twbs/rfs/master/.github/rfs-graph.svg?sanitize=true) | ||
![RFS visualisation](https://raw.githubusercontent.com/twbs/rfs/main/.github/rfs-graph.svg?sanitize=true) | ||
@@ -156,3 +489,3 @@ Each color represents another value being rescaled. For example: | ||
.title { | ||
font-size: rfs(40px); | ||
@include font-size(40px); | ||
} | ||
@@ -197,2 +530,12 @@ ``` | ||
### Breakpoint unit <sub><sup>(`px`, `em` or `rem`)</sup></sub> | ||
- SCSS, Sass & Stylus: `$rfs-breakpoint-unit` | ||
- Less: `@rfs-breakpoint-unit` | ||
- PostCSS: `breakpointUnit` | ||
The width of the max width in the media query will be rendered in this unit. | ||
*Default value: `px`* | ||
### Factor <sub><sup>(number)</sup></sub> | ||
@@ -224,36 +567,41 @@ | ||
Enabling the two dimensional option will determine the value based on the smallest side of the screen with `vmin`. This prevents values from changing if the device toggles between portrait and landscape mode. | ||
Enabling the two dimensional media queries will determine the value based on the smallest side of the screen with `vmin`. This prevents values from changing if the device toggles between portrait and landscape mode. | ||
*Default value: `false`* | ||
## Best practices | ||
### Class <sub><sup>(boolean)</sup></sub> | ||
- Don't set RFS on the `html` element, because this influences the value of `rem` and could lead to unexpected results. | ||
- Always set your line-heights relative (in `em` or unitless) to prevent interline issues with font sizes. | ||
- SCSS, Sass & Stylus: `$rfs-class` | ||
- Less: `@rfs-class` | ||
- PostCSS: `class` | ||
## Browser support | ||
RFS can be enabled or disabled with a class. There are 3 options: | ||
RFS is supported by all browsers that support [CSS math functions](https://caniuse.com/#feat=css-math-functions). If you need support for legacy browsers, check out the [v9 version](https://github.com/twbs/rfs/tree/v9.0.3) which uses media queries. | ||
- `false` | ||
No extra classes are generated. | ||
- `disable` | ||
When the the disable classes are generated you can add the `.disable-rfs` class to an element to disable responsive value rescaling for the element and its child elements. | ||
- `enable` | ||
RFS is disabled by default in this case. The `.enable-rfs` class can be added to an element to enable responsive value rescaling for the element and its child elements. | ||
## Demos | ||
*Default value: `false`* | ||
- [Card example (Sass)](https://codepen.io/MartijnCuppens/pen/NWxxWWz?editors=0100) | ||
- [Card example (Sass, with custom properties)](https://codepen.io/MartijnCuppens/pen/wvMgGpN?editors=1100) | ||
- [Card example (PostCSS)](https://codepen.io/MartijnCuppens/pen/OJMWNod?editors=0100) | ||
- [Card example (PostCSS, with custom properties)](https://codepen.io/MartijnCuppens/pen/zYrNqLN?editors=0100) | ||
- [RFS in Bootstrap demo](https://v5.getbootstrap.com/) | ||
### Safari iframe resize bug fix <sub><sup>(boolean)</sup></sub> | ||
## Versions | ||
- SCSS, Sass & Stylus: `$rfs-safari-iframe-resize-bug-fix` | ||
- Less: `@rfs-safari-iframe-resize-bug-fix` | ||
- PostCSS: `safariIframeResizeBugFix` | ||
### Current version (v10) | ||
Safari doesn't resize its values in an iframe if the iframe is resized. To fix this `min-width: 0vw` can be added and that's what happens if this option is enabled. See [#14](https://github.com/twbs/rfs/issues/14). | ||
In v10, rfs is a function which outputs the css `min()` function. | ||
*Default value: `false`* | ||
### Version 9 | ||
## Best practices | ||
In v9, rfs is a mixin in Sass, Less & Stylelint. This version uses a media query with `min-width` (mobile first approach). v9 is used in Bootstrap 5. | ||
- Don't set RFS on the `html` element, because this influences the value of `rem` and could lead to unexpected results. | ||
- Always set your line-heights relative (in `em` or unitless) to prevent interline issues with font sizes. | ||
### Version 8 | ||
## Browser support | ||
In v8, rfs only supported font sizes. This version uses a `max-width` media query. v8 is used in from Bootstrap 4.3. | ||
RFS is supported by all browsers that support [media queries](https://caniuse.com/#feat=css-mediaqueries) and [viewport units](https://caniuse.com/#feat=viewport-units). | ||
@@ -269,11 +617,9 @@ ## Creator | ||
Code released under [the MIT license](https://github.com/twbs/rfs/blob/master/LICENSE). | ||
Code released under [the MIT license](https://github.com/twbs/rfs/blob/main/LICENSE). | ||
[npm-image]: https://img.shields.io/npm/v/rfs.svg | ||
[npm-url]: https://npmjs.org/package/rfs | ||
[licence-image]: https://img.shields.io/npm/l/rfs.svg | ||
[license-url]: https://github.com/twbs/rfs/blob/master/LICENSE | ||
[build-image]: https://github.com/twbs/rfs/workflows/Tests/badge.svg | ||
[build-url]: https://github.com/twbs/rfs/actions?workflow=Tests | ||
[devDeps-image]: https://img.shields.io/david/dev/twbs/rfs.svg | ||
[devDeps-url]: https://david-dm.org/twbs/rfs?type=dev | ||
[npm-image]: https://img.shields.io/npm/v/rfs?logo=npm&logoColor=fff | ||
[npm-url]: https://www.npmjs.com/package/rfs | ||
[licence-image]: https://img.shields.io/npm/l/rfs | ||
[license-url]: https://github.com/twbs/rfs/blob/main/LICENSE | ||
[build-image]: https://img.shields.io/github/actions/workflow/status/twbs/rfs/test.yml?branch=main&label=Tests&logo=github | ||
[build-url]: https://github.com/twbs/rfs/actions?query=workflow%3ATests+branch%3Amain |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
53507
251
0
617
18
Updatedpostcss-value-parser@^4.2.0