├── .github └── workflows │ └── npm-publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── _linear-interpolation.scss ├── _list-remove.scss ├── _list-sort.scss ├── _map-sort.scss ├── _poly-fluid-sizing.scss └── package.json /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | publish: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v2 9 | with: 10 | node-version: 14 11 | #- run: npm install 12 | #- run: npm test 13 | - uses: JS-DevTools/npm-publish@v1 14 | with: 15 | token: ${{ secrets.NPM_TOKEN }} 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .sass-cache/ 2 | *.css.map 3 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jake Wilson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Poly Fluid Sizing 2 | 3 | Poly Fluid Sizing is a SASS mixin to linear interpolation size values using calc() across multiple breakpoints. It uses some basic math behind the scenes. You don't need to know the math or understand it to use this mixin. 4 | 5 | ## Usage 6 | 7 | ```scss 8 | // Import Poly Fluid Sizing mixin 9 | @import 'poly-fluid-sizing'; 10 | 11 | h1 { 12 | @include poly-fluid-sizing('font-size', (320px:18px, 768px:26px, 1024px:38px, 1440px:46px)); 13 | } 14 | ``` 15 | 16 | This outputs the following CSS (The comments are not generated and are only here for clarity) 17 | 18 | ```css 19 | h1 { 20 | /* The minimum font-size */ 21 | font-size: 18px; 22 | } 23 | @media (min-width: 320px) { 24 | h1 { 25 | /* Interpolate the font-size between 18px @ 320px and 26px @ 768px viewport */ 26 | font-size: calc(1.78571429vw + 12.28571429px); 27 | } 28 | } 29 | @media (min-width: 768px) { 30 | h1 { 31 | /* Interpolate the font-size between 26px @ 768px and 38px @ 1024px viewport */ 32 | font-size: calc(4.6875vw - 10px); 33 | } 34 | } 35 | @media (min-width: 1024px) { 36 | h1 { 37 | /* Interpolate the font-size between 38px @ 1024px and 46px @ 1440px viewport */ 38 | font-size: calc(1.92307692vw + 18.30769231px); 39 | } 40 | } 41 | @media (min-width: 1440px) { 42 | h1 { 43 | /* The maximum font-size */ 44 | font-size: 46px; 45 | } 46 | } 47 | ``` 48 | 49 | ## It can do more than `font-size` 50 | 51 | Using Poly Fluid Sizing on `font-size` is an obvious use case. But it can be used for any CSS property that uses a numeric size value. For example, `padding`, `margin`, `border-width`, `margin-left`, etc... 52 | 53 | ```sass 54 | section { 55 | @include poly-fluid-sizing('margin-right', (768px:40px, 1024px:60px)); 56 | } 57 | 58 | blockquote { 59 | @include poly-fluid-sizing('padding', (768px:30px 15px, 1024px:50px 25px)); 60 | } 61 | ``` 62 | 63 | ## SASS map order 64 | 65 | The SASS map that is passed into the mixin can be in any order. It doesn't have to be ordered from smallest viewport to largest viewport. The functions will automatically sort it for you. This is perfectly valid syntax: 66 | 67 | ```sass 68 | article { 69 | @include poly-fluid-sizing('font-size', (1024px:22px, 500px:16px, 1440px:24px, 768px:18px)); 70 | } 71 | ``` 72 | 73 | ## Limitations 74 | 75 | * You can't mix value types. For example, trying to use `2em` `font-size` @ `786px` viewport width. SASS just really won't know what to do mathematically when 1 value is using `em` and the other is using `px`. 76 | 77 | ## Coverage 78 | 79 | [Smashing Magazine: Fluid Responsive Typography With CSS Poly Fluid Sizing](https://www.smashingmagazine.com/2017/05/fluid-responsive-typography-css-poly-fluid-sizing/) 80 | 81 | [Medium.com/@jakobud CSS Poly Fluid Sizing using calc(), vw, breakpoints and linear equations](https://medium.com/@jakobud/css-polyfluidsizing-using-calc-vw-breakpoints-and-linear-equations-8e15505d21ab) 82 | 83 | ## MIT License (see LICENSE file) -------------------------------------------------------------------------------- /_linear-interpolation.scss: -------------------------------------------------------------------------------- 1 | @use "sass:math"; 2 | 3 | /// linear-interpolation 4 | /// Calculate the definition of a line between two points 5 | /// @param $map - A SASS map of viewport widths and size value pairs 6 | /// @returns A linear equation as a calc() function 7 | /// @example 8 | /// font-size: linear-interpolation((320px: 18px, 768px: 26px)); 9 | /// @author Jake Wilson 10 | @function linear-interpolation($map) { 11 | $keys: map-keys($map); 12 | @if (length($keys) != 2) { 13 | @error "linear-interpolation() $map must be exactly 2 values"; 14 | } 15 | // The slope 16 | $m: math.div(map-get($map, nth($keys, 2)) - map-get($map, nth($keys, 1)), nth($keys, 2) - nth($keys,1)); 17 | 18 | // The y-intercept 19 | $b: map-get($map, nth($keys, 1)) - $m * nth($keys, 1); 20 | 21 | // Determine if the sign should be positive or negative 22 | $sign: "+"; 23 | @if ($b < 0) { 24 | $sign: "-"; 25 | $b: abs($b); 26 | } 27 | 28 | @return calc(#{$m*100}vw #{$sign} #{$b}); 29 | } -------------------------------------------------------------------------------- /_list-remove.scss: -------------------------------------------------------------------------------- 1 | /// list-remove 2 | /// Remove an item from a list 3 | /// @param $list - A SASS list 4 | /// @param $index - The list index to remove 5 | /// @returns A SASS list 6 | /// @author Jake Wilson 7 | @function list-remove($list, $index) { 8 | $newList: (); 9 | @for $i from 1 through length($list) { 10 | @if $i != $index { 11 | $newList: append($newList, nth($list,$i), 'space'); 12 | } 13 | } 14 | @return $newList; 15 | } -------------------------------------------------------------------------------- /_list-sort.scss: -------------------------------------------------------------------------------- 1 | /// list-sort 2 | /// Sort a SASS list 3 | /// @param $list - A SASS list 4 | /// @returns A sorted SASS list 5 | /// @requires function list-remove 6 | /// @author Jake Wilson 7 | @function list-sort($list) { 8 | $sortedlist: (); 9 | @while length($list) > 0 { 10 | $value: nth($list,1); 11 | @each $item in $list { 12 | @if $item < $value { 13 | $value: $item; 14 | } 15 | } 16 | $sortedlist: append($sortedlist, $value, 'space'); 17 | $list: list-remove($list, index($list, $value)); 18 | } 19 | @return $sortedlist; 20 | } -------------------------------------------------------------------------------- /_map-sort.scss: -------------------------------------------------------------------------------- 1 | /// map-sort 2 | /// Sort map by keys 3 | /// @param $map - A SASS map 4 | /// @returns A SASS map sorted by keys 5 | /// @requires function list-sort 6 | /// @author Jake Wilson 7 | @function map-sort($map) { 8 | $keys: list-sort(map-keys($map)); 9 | $sortedMap: (); 10 | @each $key in $keys { 11 | $sortedMap: map-merge($sortedMap, ($key: map-get($map, $key))); 12 | } 13 | @return $sortedMap; 14 | } -------------------------------------------------------------------------------- /_poly-fluid-sizing.scss: -------------------------------------------------------------------------------- 1 | // Dependency functions 2 | @import 'list-remove'; 3 | @import 'list-sort'; 4 | @import 'map-sort'; 5 | @import 'linear-interpolation'; 6 | 7 | /// poly-fluid-sizing 8 | /// Generate linear interpolated size values through multiple break points 9 | /// @param $property - A string CSS property name 10 | /// @param $map - A SASS map of viewport unit and size value pairs 11 | /// @requires function linear-interpolation 12 | /// @requires function map-sort 13 | /// @example 14 | /// @include poly-fluid-sizing('font-size', (576px: 22px, 768px: 24px, 992px: 34px)); 15 | /// @author Jake Wilson 16 | @mixin poly-fluid-sizing($property, $map) { 17 | $result: (); 18 | 19 | // Get the number of provided breakpoints 20 | $length: length(map-keys($map)); 21 | 22 | // Error if the number of breakpoints is < 2 23 | @if ($length < 2) { 24 | @error "poly-fluid-sizing() $map requires at least two values"; 25 | } 26 | 27 | // Sort the map by viewport width (key) 28 | $map: map-sort($map); 29 | $keys: map-keys($map); 30 | 31 | // Minimum size 32 | #{$property}: map-get($map, nth($keys, 1)); 33 | 34 | // Interpolated size through breakpoints 35 | @for $i from 1 through ($length - 1) { 36 | $result: (); 37 | $low-values: map-get($map, nth($keys, $i)); 38 | $high-values: map-get($map, nth($keys, ($i + 1))); 39 | $total: length($low-values); 40 | $low-separator: list-separator(nth($keys, $i)); 41 | $high-separator: list-separator(nth($keys, $i + 1)); 42 | 43 | @if ($low-separator != $high-separator) { 44 | @error "poly-fluid-sizing() values must use the same separator"; 45 | } 46 | 47 | @media (min-width:nth($keys, $i)) { 48 | @if (length($low-values) != length($high-values)) { 49 | @error "poly-fluid-sizing() values must have same number args"; 50 | } 51 | 52 | @for $j from 1 through $total { 53 | $value1: nth($low-values, $j); 54 | $value2: nth($high-values, $j); 55 | $key1: nth($keys, $i); 56 | $key2: nth($keys, $i + 1); 57 | 58 | @if ($value1 != $value2) { 59 | $result: append($result, linear-interpolation(($key1: $value1, $key2: $value2)), $low-separator); 60 | } @else { 61 | $result: append($result, $value1, $low-separator); 62 | } 63 | } 64 | 65 | #{$property}: $result; 66 | } 67 | } 68 | 69 | // Maxmimum size 70 | @media (min-width:nth($keys,$length)) { 71 | #{$property}: map-get($map, nth($keys,$length)); 72 | } 73 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "poly-fluid-sizing", 3 | "version": "1.2.1", 4 | "description": "SASS mixin for linear interpolation between multiple values across multiple breakpoints using CSS calc() and viewport units", 5 | "main": "_poly-fluid-sizing.scss", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/Jakobud/poly-fluid-sizing.git" 12 | }, 13 | "keywords": [ 14 | "fluid", 15 | "typography", 16 | "scss", 17 | "sass", 18 | "css" 19 | ], 20 | "author": "Jake Wilson", 21 | "email": "jake.e.wilson@gmail.com", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/Jakobud/poly-fluid-sizing/issues" 25 | }, 26 | "homepage": "https://github.com/Jakobud/poly-fluid-sizing" 27 | } 28 | --------------------------------------------------------------------------------