├── .gitignore ├── .stylelintrc.json ├── LICENSE ├── PRINCIPLES.md ├── README.md ├── base ├── __import.scss ├── _document.scss └── _reset.scss ├── components ├── __import.scss └── _button.scss ├── objects ├── __import.scss ├── _aspect-ratio.scss ├── _container.scss ├── _position.scss └── _sr-only.scss ├── scopes ├── __import.scss └── _wysiwyg.scss ├── settings ├── __import.scss ├── _breakpoints.scss ├── _colors.scss └── _global.scss ├── style.scss ├── tools ├── __import.scss ├── _breakpoints.scss ├── _error.scss ├── _lists.scss ├── _make-class.scss ├── _make-utility.scss ├── _map-use.scss ├── _math.scss ├── _strings.scss └── _type.scss ├── type ├── __import.scss ├── _headings.scss └── _p.scss └── utilities ├── __import.scss ├── _background.scss ├── _border-radius.scss ├── _box-sizing.scss ├── _color.scss ├── _content.scss ├── _display.scss ├── _flex.scss ├── _gc-start.scss ├── _gc.scss ├── _height.scss ├── _margin.scss ├── _overflow.scss ├── _padding.scss ├── _pointer-events.scss ├── _position.scss ├── _transform.scss ├── _type.scss ├── _visibility.scss ├── _width.scss └── _z-index.scss /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "stylelint-config-standard", 4 | "stylelint-config-rational-order", 5 | "stylelint-config-recommended-scss" 6 | ], 7 | "rules": { 8 | "at-rule-empty-line-before": null, 9 | "at-rule-name-space-after": "always-single-line", 10 | "block-opening-brace-space-before": "always", 11 | "block-closing-brace-newline-after": [ 12 | "always", { 13 | "ignoreAtRules": [ "if", "else" ] 14 | } 15 | ], 16 | "color-hex-length": "long", 17 | "comment-empty-line-before": null, 18 | "declaration-empty-line-before": null, 19 | "function-parentheses-newline-inside": null, 20 | "number-leading-zero": "never", 21 | "rule-empty-line-before": [ 22 | "always", { 23 | "ignore": ["after-comment", "first-nested", "inside-block"] 24 | } 25 | ], 26 | "scss/at-else-closing-brace-newline-after": "always-last-in-chain", 27 | "scss/at-else-closing-brace-space-after": "always-intermediate", 28 | "scss/at-else-empty-line-before": "never", 29 | "scss/at-if-closing-brace-newline-after": "always-last-in-chain", 30 | "scss/at-if-closing-brace-space-after": "always-intermediate" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Lighthouse London 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 | -------------------------------------------------------------------------------- /PRINCIPLES.md: -------------------------------------------------------------------------------- 1 | # Principles 2 | 1. **Don't repeat, or unset styles** 3 | 2. **Code 'mess' has to live somewhere** 4 |


5 | ## 1. Don't repeat, or unset styles 6 | stemCSS achieves this by: 7 | ### Build classes that are specific and explicit 8 | - If a `color: black` is only applied on `:hover` at breakpoint `large`, your class is `.u-color-black@hover@large` 9 | - Which in your compiled css would be: 10 | ``` 11 | @media (min-width: 1240px) { 12 | .u-color-black\@hover\@large { 13 | color: black; 14 | } 15 | } 16 | ``` 17 | ### Too edge-case? Keep it tied to a component until you re-use it 18 | ``` 19 | .c-form { 20 | &__label { 21 | @include breakpoint(large) { 22 | &:hover { 23 | color: $colour-black; 24 | } 25 | } 26 | } 27 | } 28 | ``` 29 | 30 |
31 | 32 | ### `@extend` to apply each `property: value` to an Object or Component 33 | - The codebase creates `.u-color-black` as a Utility 34 | - Five components want to use `color: black;` 35 | - `.c-button { @extend .u-color-black; } ` 36 | - Compiled CSS: `.u-color-black, .c-button { color: black; }` 37 | 38 |
39 | 40 | ## 2. Code 'mess' has to live somewhere 41 | You can build up and style your components in your **HTML**, or in your **SCSS**. This is where your mess will live. 42 | 43 |
44 | 45 | ### Option A - Mess in the HTML 46 | 47 | ``` 48 | Submit 49 | ``` 50 | 51 |
52 | 53 | ### Option B - Mess in the SCSS 54 | ``` 55 | .c-submit { 56 | @extend .u-flex; 57 | @extend .u-ai-center; 58 | @extend .u-jc-center; 59 | @extend .u-bgcolor-green; 60 | @extend .u-shadow; 61 | @extend .u-h4; 62 | @extend .u-weight-bold; 63 | @extend .u-family-roboto; 64 | } 65 | ``` 66 | 67 |
68 | 69 | ### Option C - A bit of both 70 | ``` 71 | Submit 72 | 73 | .c-submit { 74 | @extend .u-flex; 75 | @extend .u-ai-center; 76 | @extend .u-jc-center; 77 | @extend .u-h4; 78 | @extend .u-weight-bold; 79 | @extend .u-family-roboto; 80 | } 81 | ``` 82 | 83 | ### The decision is just 'where do I prefer to manage these 'sets' of classes? 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stemCSS 2 | An [OOCSS](https://www.keycdn.com/blog/oocss) Sass framework for rapidly creating organised, responsive, and consistent user interfaces. Originally based off an early fork of [iotaCSS](https://www.iotacss.com/), stemCSS has grown into it's own thing, with a focus on code reuse and flexible programatically created utilities. 3 | 4 | ## Getting Started 5 | 1. Download the [latest release]() _- this link is TODO when v1 is done!_ 6 | 7 | ``` 8 | $ curl -o stemCSS.zip https://codeload.github.com/wearelighthouse/stemCSS/zip/master 9 | ``` 10 | 11 | 2. Get the contents of it into an assets folder in your project. E.g. 12 | 13 | ``` 14 | $ unzip -q stemCSS.zip 15 | $ rm -r stemCSS.zip 16 | $ mkdir -p assets/scss 17 | $ cp -r stemCSS-master/* assets/scss/ 18 | $ rm -r stemCSS-master/ 19 | ``` 20 | 21 | 3. [Use something like gulp + gulp-sass to transpile the scss](https://css-tricks.com/gulp-for-beginners/). 22 | 23 | 24 | # Utilities 25 | Class or placeholder selectors that produces a single `value: property`. 26 | ``` 27 | .u-color-black { color: black; } 28 | .u-flex { display: flex; } 29 | .u-pos-absolute { position: absolute; } 30 | ``` 31 | 32 | _In some circumstances_ - like `mx` (`margin-left`, `margin-right`), a utility produces two or more properties. 33 | ``` 34 | .u-mx-auto { 35 | margin-left: auto; 36 | margin-right: auto; 37 | } 38 | ``` 39 | 40 | Utilities are "created" by passing a sass map (hence the double brackets) into the make-utility mixin: 41 | ``` 42 | @include make-utility((...)); 43 | ``` 44 | The items in the map can include an __alias__ to determine the name of the utility, a __class__ flag which creates the utility as a class which is always included in the transpiled CSS and can be used in the HTML (rather than just a placeholder which _cannot_ be used in the HTML, and is only in the transpiled CSS if it gets extended), and a __set of CSS properties__. 45 | ``` 46 | @include make-utility(( 47 | alias: 'mx-auto', 48 | class: true, 49 | margin-left: auto, 50 | margin-right: auto 51 | )); 52 | ``` 53 | If an __alias__ is not included in the sass map, the first CSS property and value is used as the name of the utility, e.g. 54 | ``` 55 | @include make-utility(( 56 | height: 1px 57 | )); 58 | ``` 59 | ... can be used with: 60 | ``` 61 | @extend %u-height-1px; 62 | ``` 63 | 64 | See examples in the pre-existing utilties: [`/utilties/*.scss`](https://github.com/wearelighthouse/stemCSS/tree/master/utilities). 65 | 66 | 67 | # Objects 68 | Layouts objects - grids, containers, and helpful sets of classes like aspect-ratio etc. 69 | Never *visual* - you can't picture an object because it has no colours. If it has _colours_ - it's _probably_ a component. 70 | Objects typically @extend utilities. 71 | ``` 72 | .o-container { 73 | @extend .u-mx-auto; 74 | width: 90vw; 75 | } 76 | ``` 77 | 78 | # Components 79 | *Visual components* in your design system. `Header, Button, Footer, Hero` etc. 80 | Follow the *BEM* methodology for their children _where appropriate_. 81 | ``` 82 | .c-button { 83 | @extend .u-flex; 84 | @extend .u-bg-color-black; 85 | @extend .u-color-white; 86 | 87 | &__icon { 88 | @extend .u-pos-absolute; 89 | @extend .u-left-50pc; 90 | transform: translateX(-50%); 91 | } 92 | } 93 | ``` 94 | 95 | # Breakpoints 96 | 97 | ### Defining Breakpoints 98 | These are defined in `settings/_breakpoints`, which includes: 99 | 100 | General breakpoints as sass variables - can simply be used anywhere you need them! 101 | ``` 102 | $breakpoint-minimum: 320px; 103 | $breakpoint-large: 1400px; 104 | ``` 105 | 106 | Breakpoints sass map (get merged with the following map, sans any special generation) 107 | ``` 108 | $global-breakpoints: ( 109 | landscape: 'screen and (orientation: landscape)', 110 | portrait: 'screen and (orientation: portrait)' 111 | ); 112 | ``` 113 | 114 | Breakpoints sass map with automatic --from and --upto generation 115 | ``` 116 | $global-breakpoints: map-merge($global-breakpoints, make-width-breakpoints(( 117 | small: 400px, 118 | medium: 800px, 119 | large: 1200px, 120 | ))); 121 | ``` 122 | 123 | ### Using Breakpoints 124 | 125 | ##### iotaCSS style 126 | ``` 127 | @include breakpoint(from-medium) { 128 | color: red; 129 | } 130 | ``` 131 | 132 | ##### stemCSS style 133 | ``` 134 | @extend %u-color-red--from-medium; 135 | ``` 136 | The main advantage of using "stemCSS style" breakpoints are that they are extending placeholders, rather than requiring additional individual CSS rules (i.e. you cannot `@extend` a utility from within a standard iotaCSS breakpoint). 137 | 138 | If, for example for consistency, you wanted to restrict the available widths to only 0%, 50%, or 100%, and had created those options in the `_width.scss` utility, then using `@extend %u-width-60pc--from-medium` would fail, whereas `@include breakpoint(from-medium) { width: 60% }` would obviously not. 139 | 140 | # Colors 141 | 142 | Text and background color utilities and are very similar to [iotaCSS's](https://www.iotacss.com/docs/utilities/color/). Colors are defined in [`settings/_colors.scss`](https://github.com/wearelighthouse/stemCSS/blob/master/settings/_colors.scss). E.g: 143 | ``` 144 | $color-very-dark: #111111; 145 | $auto-colors: ( 146 | 'black': $color-very-dark 147 | } 148 | ``` 149 | Allows you to use both: `@extend %u-color-black;` and `@extend %u-bg-color-black;` 150 | -------------------------------------------------------------------------------- /base/__import.scss: -------------------------------------------------------------------------------- 1 | @import 2 | 'reset', 3 | 'document'; 4 | -------------------------------------------------------------------------------- /base/_document.scss: -------------------------------------------------------------------------------- 1 | body { 2 | @include font-scale(14, 22); 3 | } 4 | -------------------------------------------------------------------------------- /base/_reset.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Resets and/or normalizes styles for commonly used HTML elements. 3 | * Intended for modern versions of Chrome, Firefox, Edge, Safari, and IE11. 4 | * 5 | * Inspired by normalize.css v8.0.1 and Eric Meyer’s Reset CSS 2.0 6 | * - But easy to swap out for either of those in full if required! 7 | */ 8 | 9 | // Remove margins on body 10 | body { 11 | margin: 0; 12 | } 13 | 14 | // Normalize global line-height 15 | html { 16 | line-height: 1.15; 17 | } 18 | 19 | // Render `
` correctly in IE 20 | main { 21 | display: block; 22 | } 23 | 24 | // Reset heading font-weight 25 | h1, 26 | h2, 27 | h3, 28 | h4, 29 | h5, 30 | h6 { 31 | font-weight: normal; 32 | } 33 | 34 | // Remove list markers 35 | li { 36 | list-style: none; 37 | } 38 | 39 | // Remove link styling 40 | a { 41 | color: inherit; 42 | text-decoration: none; 43 | } 44 | 45 | // Reset some button styling 46 | button { 47 | margin: 0; 48 | padding: 0; 49 | font: inherit; 50 | background: none; 51 | border: none; 52 | } 53 | -------------------------------------------------------------------------------- /components/__import.scss: -------------------------------------------------------------------------------- 1 | @import 2 | 'button'; 3 | -------------------------------------------------------------------------------- /components/_button.scss: -------------------------------------------------------------------------------- 1 | .c-button { 2 | @extend %u-mx-auto; 3 | } 4 | -------------------------------------------------------------------------------- /objects/__import.scss: -------------------------------------------------------------------------------- 1 | @import 2 | 'aspect-ratio', 3 | 'container', 4 | 'sr-only', 5 | 'position'; 6 | -------------------------------------------------------------------------------- /objects/_aspect-ratio.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Specify an aspect ratio for an element, and it will 3 | * figure out it's own height based off it's width. E.g. 4 | * `
` 5 | * 6 | * To use a specific aspect ratio it must be included in the ratio list 7 | * `((horizontal:vertical), (...), (...), ...)` 8 | */ 9 | @each $ratio in ((16:9), (2:1), (1:1)) { 10 | $h: first(map-keys($ratio)); 11 | $v: first(map-values($ratio)); 12 | 13 | #{'.o-aspect-ratio--' + $h + '-' + $v} { 14 | @extend %u-pos-relative; 15 | 16 | &::before { 17 | @extend %u-block; 18 | @extend %u-content-none; 19 | @extend #{'%u-width-100' + $global-unit-percent}; 20 | padding-top: #{get-percent-from-list(($h, $v)) * 1%}; 21 | } 22 | 23 | > * { 24 | @extend %u-pos-absolute; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /objects/_container.scss: -------------------------------------------------------------------------------- 1 | .o-container { 2 | @extend .u-mx-auto; 3 | width: 90vw; 4 | } 5 | -------------------------------------------------------------------------------- /objects/_position.scss: -------------------------------------------------------------------------------- 1 | .o-absfill { 2 | @extend %u-pos-absolute; 3 | @extend %u-top; 4 | @extend %u-left; 5 | @extend %u-width-100pc; 6 | @extend %u-height-100pc; 7 | } 8 | 9 | .o-abscentre { 10 | @extend %u-pos-absolute; 11 | @extend %u-top-50pc; 12 | @extend %u-left-50pc; 13 | @extend %u-translate--50pc; 14 | } 15 | 16 | .o-abscentre-x { 17 | @extend %u-pos-absolute; 18 | @extend %u-left-50pc; 19 | @extend %u-translateX--50pc; 20 | } 21 | 22 | .o-abscentre-y { 23 | @extend %u-pos-absolute; 24 | @extend %u-top-50pc; 25 | @extend %u-translateY--50pc; 26 | } 27 | -------------------------------------------------------------------------------- /objects/_sr-only.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Visually hidden, but provided for screen readers (dictated). 3 | * Similar to WordPress .screen-reader-text: 4 | * https://make.wordpress.org/accessibility/handbook/markup/the-css-class-screen-reader-text/ 5 | */ 6 | .o-sr-only { 7 | @extend %u-pos-absolute; 8 | @extend %u-width-1px; 9 | @extend %u-height-1px; 10 | @extend %u-ov-hidden; 11 | top: -1px; 12 | left: -1px; 13 | margin: -1px; 14 | padding: 0; 15 | border: 0; 16 | clip-path: inset(50%); 17 | } 18 | -------------------------------------------------------------------------------- /scopes/__import.scss: -------------------------------------------------------------------------------- 1 | @import 2 | 'wysiwyg'; 3 | -------------------------------------------------------------------------------- /scopes/_wysiwyg.scss: -------------------------------------------------------------------------------- 1 | .s-wysiwyg { 2 | p { 3 | @extend .type-p; 4 | margin-bottom: 1rem; 5 | } 6 | 7 | a { 8 | text-decoration: underline; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /settings/__import.scss: -------------------------------------------------------------------------------- 1 | @import 2 | 'global', 3 | 'breakpoints', 4 | 'colors'; 5 | -------------------------------------------------------------------------------- /settings/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | $breakpoint-minimum: 320px; 2 | $breakpoint-large: 1400px; 3 | 4 | $breakpoint-smallest-font: $breakpoint-minimum; 5 | $breakpoint-largest-font: $breakpoint-large; 6 | 7 | $global-breakpoints: ( 8 | landscape: 'screen and (orientation: landscape)', 9 | portrait: 'screen and (orientation: portrait)' 10 | ); 11 | 12 | /** 13 | * User specified breakpoint "boundaries". 14 | * 15 | * Adds width breakpoints to the $global-breakpoints map with: 16 | * 17 | * breakpoint-a (as a min-width media query) 18 | * from-breakpoint-a (as a min-width media query) 19 | * upto-breakpoint-a (as a max-width media query) 20 | * 21 | * ... and all possible combinations of: 22 | * 23 | * from-breakpoint-a--upto-breakpoint-b (where breakpoint b > breakpoint a) 24 | */ 25 | $global-breakpoints: map-merge( 26 | $global-breakpoints, 27 | make-width-breakpoints(( 28 | small: 400px, 29 | medium: 800px, 30 | large: 1200px, 31 | )) 32 | ); 33 | 34 | // Print all the breakpoints to check they have been generated as intended 35 | // @each $breakpoint-key, $breakpoint-value in $global-breakpoints { 36 | // @debug $breakpoint-key + ': ' + $breakpoint-value; 37 | // } 38 | -------------------------------------------------------------------------------- /settings/_colors.scss: -------------------------------------------------------------------------------- 1 | // Global color variables 2 | $color-white: #ffffff; 3 | $color-black: #000000; 4 | 5 | // Colors used to automatically generate font and background color utilities. 6 | $auto-colors: ( 7 | 'white': $color-white, 8 | 'black': $color-black, 9 | 'transparent': transparent, 10 | 'currentColor': currentColor 11 | ); 12 | -------------------------------------------------------------------------------- /settings/_global.scss: -------------------------------------------------------------------------------- 1 | $global-everything-as-class: true; 2 | $global-breakpoint-separator: '--'; 3 | $global-pseudo-separator: \@; 4 | $global-unit-percent: 'pc'; 5 | $global-grid-columns: 12; 6 | $global-size-step: .25rem; 7 | $global-size-range: 16; 8 | -------------------------------------------------------------------------------- /style.scss: -------------------------------------------------------------------------------- 1 | @import 2 | 'tools/_import', 3 | 'settings/_import', 4 | 'base/_import', 5 | 'utilities/_import', 6 | 'objects/_import', 7 | 'scopes/_import', 8 | 'type/_import', 9 | 'components/_import'; 10 | -------------------------------------------------------------------------------- /tools/__import.scss: -------------------------------------------------------------------------------- 1 | @import 2 | 'breakpoints', 3 | 'error', 4 | 'lists', 5 | 'make-class', 6 | 'make-utility', 7 | 'map-use', 8 | 'math', 9 | 'strings', 10 | 'type'; 11 | -------------------------------------------------------------------------------- /tools/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Adds an automatically generated list of screen width 3 | * breakpoints to the $global-breakpoints map with: 4 | * 5 | * from-breakpoint-a (as a min-width media query) 6 | * upto-breakpoint-a (as a max-width media query) 7 | * 8 | * ... and all possible combinations of: 9 | * 10 | * from-breakpoint-a--upto-breakpoint-b (where breakpoint b > breakpoint a) 11 | * 12 | */ 13 | 14 | @function min-width($value) { 15 | @return '(min-width: ' + $value + ')'; 16 | } 17 | 18 | @function max-width($value) { 19 | @return '(max-width: ' + ($value - 1px) + ')'; 20 | } 21 | 22 | @function make-width-breakpoints($width-breakpoints) { 23 | $output-breakpoints: (); 24 | 25 | @each $bp-a-key, $bp-a-value in $width-breakpoints { 26 | $a: index(($width-breakpoints), ($bp-a-key $bp-a-value)); 27 | 28 | $output-breakpoints: map-merge( 29 | $output-breakpoints, 30 | ( 31 | 'upto-' + $bp-a-key: 32 | 'screen and ' + max-width($bp-a-value) 33 | ) 34 | ); 35 | 36 | $output-breakpoints: map-merge( 37 | $output-breakpoints, 38 | ( 39 | 'from-' + $bp-a-key: 40 | 'screen and ' + min-width($bp-a-value) 41 | ) 42 | ); 43 | 44 | @each $bp-b-key, $bp-b-value in $width-breakpoints { 45 | $b: index(($width-breakpoints), ($bp-b-key $bp-b-value)); 46 | 47 | @if $a != $b and $bp-b-value > $bp-a-value { 48 | $output-breakpoints: map-merge( 49 | $output-breakpoints, 50 | ( 51 | 'from-' + $bp-a-key + '--upto-' + $bp-b-key: 52 | 'screen and ' + min-width($bp-a-value) + ' and ' + max-width($bp-b-value) 53 | ) 54 | ); 55 | } 56 | } 57 | } 58 | 59 | @return $output-breakpoints; 60 | } 61 | 62 | /** 63 | * A breakpoint mixin. 64 | * Source: IotaCSS iota-breakpoint 65 | */ 66 | @mixin breakpoint($size, $breakpoints: $global-breakpoints) { 67 | $breakpoint-found: map-has-key($breakpoints, $size); 68 | 69 | @if ($breakpoint-found == true) { 70 | $breakpoint: map-get($breakpoints, $size); 71 | 72 | @media #{$breakpoint} { 73 | @content; 74 | } 75 | } @else { 76 | @warn 'Breakpoint size \'#{$size}\' doesn\'t exist.'; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tools/_error.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Gives an element an :after pseudo, with an error message. 3 | * 4 | * Used with gulp-replace or similar build tool to removed it for production, e.g: 5 | * (just before gulp-clean-css) 6 | * .pipe(replace(/(\/\* dev-only:start \*\/[^]*\/\* dev-only:end \*\/)/g, '')) 7 | */ 8 | @mixin error($message) { 9 | &::after { 10 | /* dev-only:start */ 11 | width: 100%; 12 | font-size: 14px; 13 | font-family: monospace; 14 | content: 'Error: ' + $message; 15 | /* dev-only:end */ 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tools/_lists.scss: -------------------------------------------------------------------------------- 1 | @function first($list) { 2 | @return nth($list, 1); 3 | } 4 | 5 | @function last($list) { 6 | @return nth($list, length($list)); 7 | } 8 | -------------------------------------------------------------------------------- /tools/_make-class.scss: -------------------------------------------------------------------------------- 1 | @mixin make-class($selector, $properties, $value) { 2 | #{$selector} { 3 | @each $property in $properties { 4 | #{$property}: #{$value}; 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tools/_make-utility.scss: -------------------------------------------------------------------------------- 1 | @mixin make-class-from-placeholder($utility-name, $properties) { 2 | // For no breakpoint 3 | .#{$utility-name} { 4 | @extend %#{$utility-name}; 5 | } 6 | 7 | // For every breakpoint 8 | // Disabled because it creates a TON of styles in style.css, because can't extend within @medias. 9 | // @each $breakpoint-key, $breakpoint-value in $global-breakpoints { 10 | // .#{$utility-name + '--' + $breakpoint-key} { 11 | // @include breakpoint($breakpoint-key) { 12 | // @each $property, $value in $properties { 13 | // #{$property}: $value; 14 | // } 15 | // } 16 | // } 17 | // } 18 | } 19 | 20 | @mixin make-placeholder($utility-name, $properties) { 21 | // For no breakpoint 22 | %#{$utility-name} { 23 | @each $property, $value in $properties { 24 | #{$property}: $value; 25 | } 26 | } 27 | 28 | // For every breakpoint 29 | @each $breakpoint-key, $breakpoint-value in $global-breakpoints { 30 | %#{$utility-name + $global-breakpoint-separator + $breakpoint-key} { 31 | @include breakpoint($breakpoint-key) { 32 | @each $property, $value in $properties { 33 | #{$property}: $value; 34 | } 35 | } 36 | } 37 | } 38 | } 39 | 40 | @mixin make-utility($args) { 41 | $class: map-use( 42 | $args, 43 | class, 44 | false 45 | ); 46 | 47 | $args: map-remove($args, class); 48 | 49 | // If 'alias' key exists in $args, use that for the placeholder-name, 50 | // otherwise use the key and value of the first property in $args 51 | $utility-name: map-use( 52 | $args, 53 | alias, 54 | first(map-keys($args)) + '-' + first(map-values($args)) 55 | ); 56 | 57 | $utility-name: 'u-' + $utility-name; 58 | 59 | $properties: map-remove($args, alias); 60 | 61 | // Debug output to list off all the placeholders being made: 62 | // @if ($class) { 63 | // @debug('Making placeholder & class: ' + $utility-name); 64 | // } else { 65 | // @debug('Making placeholder: ' + $utility-name); 66 | // } 67 | 68 | @include make-placeholder($utility-name, $properties); 69 | 70 | @if ($class or $global-everything-as-class) { 71 | @include make-class-from-placeholder($utility-name, $properties); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tools/_map-use.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * If a map contains a specific key, return it's value, else return a fallback. 3 | * 4 | * For example: 5 | * $myMap: ('a': 'foo', 'b': 'bar'); 6 | * property: map-use($myMap, a, 'that key is not in the map'); 7 | * 8 | * Gives the results: 9 | * property: 'foo'; 10 | * 11 | * If there were no 'a' key then the reults would be: 12 | * property: 'that key is not in the map'; 13 | */ 14 | @function map-use($map, $key, $fallback) { 15 | @return if(map-has-key($map, $key), map-get($map, $key), $fallback); 16 | } 17 | -------------------------------------------------------------------------------- /tools/_math.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Get a percentage value of the second value from a first, in a list. 3 | * 4 | * E.g. get-percent-from-list((16, 4)) returns 25, as 4 is 25% of 16 5 | */ 6 | @function get-percent-from-list($list) { 7 | @return (100 / nth($list, 1) * nth($list, 2)); 8 | } 9 | -------------------------------------------------------------------------------- /tools/_strings.scss: -------------------------------------------------------------------------------- 1 | @function str-replace($string, $search, $replace: '') { 2 | $index: str-index($string, $search); 3 | 4 | @if $index { 5 | $before: str-slice($string, 1, $index - 1); 6 | $after: str-replace(str-slice($string, $index + str-length($search)), $search, $replace); 7 | @return $before + $replace + $after; 8 | } 9 | 10 | @return $string; 11 | } 12 | 13 | @function to-string($list, $glue: '', $is-nested: false) { 14 | $result: null; 15 | 16 | @for $i from 1 through length($list) { 17 | $e: nth($list, $i); 18 | 19 | @if type-of($e) == list { 20 | $result: $result#{to-string($e, $glue, true)}; 21 | } @else { 22 | $result: if($i != length($list) or $is-nested, $result#{$e}#{$glue}, $result#{$e}); 23 | } 24 | } 25 | 26 | @return $result; 27 | } 28 | 29 | @function strip-unit($number) { 30 | @if type-of($number) == 'number' and not unitless($number) { 31 | @return $number / ($number * 0 + 1); 32 | } 33 | 34 | @return $number; 35 | } 36 | 37 | @function to-px($number) { 38 | /* stylelint-disable-next-line length-zero-no-unit */ 39 | @return $number + 0px; 40 | } 41 | 42 | @function to-rem($number) { 43 | /* stylelint-disable-next-line length-zero-no-unit */ 44 | @return $number + 0rem; 45 | } 46 | -------------------------------------------------------------------------------- /tools/_type.scss: -------------------------------------------------------------------------------- 1 | @mixin font-scale($smallest, $largest) { 2 | font-size: #{to-px($smallest)}; 3 | 4 | @media (min-width: #{to-px($breakpoint-smallest-font)}) and (max-width: #{to-px($breakpoint-largest-font)}) { 5 | $font-diff: strip-unit($largest) - strip-unit($smallest); 6 | $breakpoint-diff: strip-unit($breakpoint-largest-font) - strip-unit($breakpoint-smallest-font); 7 | $vw-minus-smallest-breakpoint: 100vw - #{to-px($breakpoint-smallest-font)}; 8 | 9 | font-size: calc(#{to-px($smallest)} + #{$font-diff} * (#{$vw-minus-smallest-breakpoint}) / #{$breakpoint-diff}); 10 | } 11 | 12 | @media (min-width: #{to-px($breakpoint-largest-font)}) { 13 | font-size: #{to-px($largest)}; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /type/__import.scss: -------------------------------------------------------------------------------- 1 | @import 2 | 'headings', 3 | 'p'; 4 | -------------------------------------------------------------------------------- /type/_headings.scss: -------------------------------------------------------------------------------- 1 | @for $level from 1 through 5 { 2 | .type-h#{$level} { 3 | line-height: 1.25; 4 | } 5 | } 6 | 7 | .type-h1 { 8 | @include font-scale(34, 40); 9 | } 10 | 11 | .type-h2 { 12 | @include font-scale(30, 34); 13 | } 14 | 15 | .type-h3 { 16 | @include font-scale(28, 32); 17 | } 18 | -------------------------------------------------------------------------------- /type/_p.scss: -------------------------------------------------------------------------------- 1 | .type-p { 2 | @include font-scale(16, 18); 3 | line-height: 1.5; 4 | } 5 | -------------------------------------------------------------------------------- /utilities/__import.scss: -------------------------------------------------------------------------------- 1 | @import 2 | 'background', 3 | 'border-radius', 4 | 'box-sizing', 5 | 'color', 6 | 'content', 7 | 'display', 8 | 'flex', 9 | 'gc', 10 | 'gc-start', 11 | 'height', 12 | 'margin', 13 | 'overflow', 14 | 'padding', 15 | 'pointer-events', 16 | 'position', 17 | 'transform', 18 | 'type', 19 | 'visibility', 20 | 'width', 21 | 'z-index'; 22 | -------------------------------------------------------------------------------- /utilities/_background.scss: -------------------------------------------------------------------------------- 1 | @each $key, $value in ($auto-colors) { 2 | @include make-utility(( 3 | alias: 'bg-color-' + $key, 4 | class: true, 5 | background-color: $value 6 | )); 7 | } 8 | 9 | @each $value in (cover, contain) { 10 | @include make-utility(( 11 | alias: 'bg-size-' + $value, 12 | background-size: $value 13 | )); 14 | } 15 | -------------------------------------------------------------------------------- /utilities/_border-radius.scss: -------------------------------------------------------------------------------- 1 | @include make-utility(( 2 | alias: rounded, 3 | class: true, 4 | border-radius: .25rem 5 | )); 6 | 7 | @include make-utility(( 8 | alias: round, 9 | class: true, 10 | border-radius: 50% 11 | )); 12 | 13 | @include make-utility(( 14 | alias: pill, 15 | class: true, 16 | border-radius: 1e3px 17 | )); 18 | -------------------------------------------------------------------------------- /utilities/_box-sizing.scss: -------------------------------------------------------------------------------- 1 | @include make-utility(( 2 | alias: border-box, 3 | class: true, 4 | box-sizing: border-box 5 | )); 6 | 7 | @include make-utility(( 8 | alias: content-box, 9 | class: true, 10 | box-sizing: content-box 11 | )); 12 | -------------------------------------------------------------------------------- /utilities/_color.scss: -------------------------------------------------------------------------------- 1 | @each $name, $hex in ($auto-colors) { 2 | @include make-utility(( 3 | alias: 'color-' + $name, 4 | class: true, 5 | color: $hex 6 | )); 7 | } 8 | -------------------------------------------------------------------------------- /utilities/_content.scss: -------------------------------------------------------------------------------- 1 | // Note that the utility alias MUST be the key of this map, because identical keys are not allowed 2 | $content-utilities: ( 3 | '': '', 4 | '-none': '', 5 | '-colon': ':' 6 | ); 7 | 8 | @each $alias, $value in ($content-utilities) { 9 | @include make-utility(( 10 | alias: 'content' + $alias, 11 | content: $value 12 | )); 13 | } 14 | -------------------------------------------------------------------------------- /utilities/_display.scss: -------------------------------------------------------------------------------- 1 | $aliases: ( 2 | 'flex', 3 | 'inline-flex', 4 | 'inline', 5 | 'inline-block', 6 | 'grid', 7 | 'inline-grid', 8 | 'block', 9 | 'display-none', 10 | 'contents' 11 | ); 12 | 13 | @each $alias in $aliases { 14 | $value: str-replace($alias, 'display-'); 15 | @include make-utility(( 16 | alias: $alias, 17 | class: true, 18 | display: unquote($value) 19 | )); 20 | } 21 | 22 | @include make-utility(( 23 | alias: border-box, 24 | class: true, 25 | box-sizing: border-box 26 | )); 27 | -------------------------------------------------------------------------------- /utilities/_flex.scss: -------------------------------------------------------------------------------- 1 | @each $direction in (row, row-reverse, column, column-reverse) { 2 | @include make-utility(( 3 | alias: 'fd-' + $direction, 4 | flex-direction: $direction 5 | )); 6 | } 7 | 8 | @each $wrap in (nowrap, wrap, wrap-reverse) { 9 | @include make-utility(( 10 | alias: 'fw-' + $wrap, 11 | flex-wrap: $wrap 12 | )); 13 | } 14 | 15 | @each $direction in (row, row-reverse, column, column-reverse) { 16 | @each $wrap in (nowrap, wrap, wrap-reverse) { 17 | @include make-utility(( 18 | alias: 'ff-' + $direction + '-' + $wrap, 19 | flex-flow: $direction + ' ' + $wrap 20 | )); 21 | } 22 | } 23 | 24 | @each $value in (flex-start, flex-end, center, space-between, space-around) { 25 | @include make-utility(( 26 | alias: 'jc-' + $value, 27 | class: true, 28 | justify-content: $value 29 | )); 30 | } 31 | 32 | @each $value in (center) { 33 | @include make-utility(( 34 | alias: 'ji-' + $value, 35 | class: true, 36 | justify-items: $value 37 | )); 38 | } 39 | 40 | @each $value in (flex-start, flex-end, center, stretch, baseline) { 41 | @include make-utility(( 42 | alias: 'ai-' + $value, 43 | class: true, 44 | align-items: $value 45 | )); 46 | } 47 | 48 | @include make-utility(( 49 | alias: 'fb-0', 50 | flex-basis: 0 51 | )); 52 | 53 | @each $value in (0, 1) { 54 | @include make-utility(( 55 | alias: 'fs-' + $value, 56 | flex-shrink: $value 57 | )); 58 | 59 | @include make-utility(( 60 | alias: 'fg-' + $value, 61 | flex-grow: $value 62 | )); 63 | } 64 | -------------------------------------------------------------------------------- /utilities/_gc-start.scss: -------------------------------------------------------------------------------- 1 | @for $i from -1 through $global-grid-columns { 2 | @include make-utility(( 3 | alias: 'gc-start-' + $i, 4 | grid-column-start: $i 5 | )); 6 | } 7 | -------------------------------------------------------------------------------- /utilities/_gc.scss: -------------------------------------------------------------------------------- 1 | $columns: $global-grid-columns; 2 | 3 | @for $start from 1 through $columns { 4 | @for $span from 1 through $columns { 5 | // Only add object if the specified values don't "overflow" out of the grid 6 | @if $start + $span < $columns + 2 { 7 | @include make-utility(( 8 | alias: 'gc-' + $start + '-' + $span, 9 | grid-column: $start + ' / span ' + $span, 10 | width: 100% 11 | )); 12 | } 13 | } 14 | } 15 | 16 | @include make-utility(( 17 | alias: 'gc-full', 18 | grid-column: '1 / -1' 19 | )); 20 | -------------------------------------------------------------------------------- /utilities/_height.scss: -------------------------------------------------------------------------------- 1 | @include make-utility(( 2 | alias: 'height-100' + $global-unit-percent, 3 | class: true, 4 | height: 100% 5 | )); 6 | 7 | @include make-utility(( 8 | height: 1px 9 | )); 10 | -------------------------------------------------------------------------------- /utilities/_margin.scss: -------------------------------------------------------------------------------- 1 | @mixin create-margin-utils($value, $alias: $value) { 2 | @include make-utility(( 3 | alias: 'm-' + $alias, 4 | class: true, 5 | margin: $value 6 | )); 7 | 8 | @include make-utility(( 9 | alias: 'mx-' + $alias, 10 | class: true, 11 | margin-right: $value, 12 | margin-left: $value 13 | )); 14 | 15 | @include make-utility(( 16 | alias: 'my-' + $alias, 17 | class: true, 18 | margin-top: $value, 19 | margin-bottom: $value 20 | )); 21 | 22 | @include make-utility(( 23 | alias: 'mt-' + $alias, 24 | class: true, 25 | margin-top: $value 26 | )); 27 | 28 | @include make-utility(( 29 | alias: 'mr-' + $alias, 30 | class: true, 31 | margin-right: $value 32 | )); 33 | 34 | @include make-utility(( 35 | alias: 'mb-' + $alias, 36 | class: true, 37 | margin-bottom: $value 38 | )); 39 | 40 | @include make-utility(( 41 | alias: 'ml-' + $alias, 42 | class: true, 43 | margin-left: $value 44 | )); 45 | } 46 | 47 | // Create margin utilities for -1...-16, then 1..16 48 | @each $sign in -1, 1 { 49 | @for $i from 1 through $global-size-range { 50 | $alias: $sign * $i; 51 | $value: $alias * $global-size-step; 52 | @include create-margin-utils($value, $alias); 53 | } 54 | } 55 | 56 | // Create margin utilities for auto (e.g. u-mx-auto), then 0 (unitless) 57 | @each $value in auto, 0 { 58 | @include create-margin-utils($value); 59 | } 60 | -------------------------------------------------------------------------------- /utilities/_overflow.scss: -------------------------------------------------------------------------------- 1 | $overflow-properties: ( 2 | ov: overflow, 3 | ov-x: overflow-x, 4 | ov-y: overflow-y 5 | ); 6 | 7 | $overflow-values-classes: ( 8 | visible: false, 9 | hidden: true, 10 | scroll: false, 11 | auto: false 12 | ); 13 | 14 | @each $property-alias, $property in ($overflow-properties) { 15 | @each $value, $is-class in ($overflow-values-classes) { 16 | @include make-utility(( 17 | alias: $property-alias + '-' + $value, 18 | class: $is-class, 19 | $property: $value 20 | )); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /utilities/_padding.scss: -------------------------------------------------------------------------------- 1 | @mixin create-padding-utils($value, $alias: $value) { 2 | @include make-utility(( 3 | alias: 'p-' + $alias, 4 | class: true, 5 | padding: $value 6 | )); 7 | 8 | @include make-utility(( 9 | alias: 'px-' + $alias, 10 | class: true, 11 | padding-right: $value, 12 | padding-left: $value 13 | )); 14 | 15 | @include make-utility(( 16 | alias: 'py-' + $alias, 17 | class: true, 18 | padding-top: $value, 19 | padding-bottom: $value 20 | )); 21 | 22 | @include make-utility(( 23 | alias: 'pt-' + $alias, 24 | class: true, 25 | padding-top: $value 26 | )); 27 | 28 | @include make-utility(( 29 | alias: 'pr-' + $alias, 30 | class: true, 31 | padding-right: $value 32 | )); 33 | 34 | @include make-utility(( 35 | alias: 'pb-' + $alias, 36 | class: true, 37 | padding-bottom: $value 38 | )); 39 | 40 | @include make-utility(( 41 | alias: 'pl-' + $alias, 42 | class: true, 43 | padding-left: $value 44 | )); 45 | } 46 | 47 | // Create padding utilities for -1...-16, then 1..16 48 | @each $sign in -1, 1 { 49 | @for $i from 1 through $global-size-range { 50 | $alias: $sign * $i; 51 | $value: $alias * $global-size-step; 52 | @include create-padding-utils($value, $alias); 53 | } 54 | } 55 | 56 | // Create padding utilities for 0 (unitless) 57 | @include create-padding-utils(0); 58 | -------------------------------------------------------------------------------- /utilities/_pointer-events.scss: -------------------------------------------------------------------------------- 1 | @include make-utility(( 2 | alias: 'events-none', 3 | class: true, 4 | pointer-events: none 5 | )); 6 | 7 | @include make-utility(( 8 | alias: 'events-all', 9 | class: true, 10 | pointer-events: all 11 | )); 12 | -------------------------------------------------------------------------------- /utilities/_position.scss: -------------------------------------------------------------------------------- 1 | @each $value in (absolute, relative) { 2 | @include make-utility(( 3 | alias: 'pos-' + $value, 4 | class: true, 5 | position: $value 6 | )); 7 | } 8 | 9 | @each $property in (top, right, bottom, left) { 10 | // E.g. `@extend %u-right;` To align to right 11 | @include make-utility(( 12 | class: true, 13 | alias: $property, 14 | $property: 0 15 | )); 16 | 17 | @each $value in (50, 100) { 18 | @include make-utility(( 19 | alias: $property + '-' + $value + $global-unit-percent, 20 | class: true, 21 | $property: $value * 1% 22 | )); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /utilities/_transform.scss: -------------------------------------------------------------------------------- 1 | @each $property in (translateX, translateY) { 2 | @each $value in (-100, -50, 0, 50, 100) { 3 | @include make-utility(( 4 | alias: $property + '-' + $value + $global-unit-percent, 5 | transform: $property + '(' + $value + '%)' 6 | )); 7 | } 8 | } 9 | 10 | @each $value in (-100, -50, 0, 50, 100) { 11 | @include make-utility(( 12 | alias: 'translate-' + $value + $global-unit-percent, 13 | transform: unquote('translate' + '(' + $value + '%, ' + $value + '%)') 14 | )); 15 | } 16 | -------------------------------------------------------------------------------- /utilities/_type.scss: -------------------------------------------------------------------------------- 1 | @each $value in (capitalize, lowercase, uppercase) { 2 | @include make-utility(( 3 | alias: $value, 4 | text-transform: $value, 5 | class: true 6 | )); 7 | } 8 | 9 | @each $value in (left, center, right) { 10 | @include make-utility(( 11 | text-align: $value 12 | )); 13 | } 14 | 15 | @include make-utility(( 16 | alias: underline, 17 | text-decoration: underline 18 | )); 19 | -------------------------------------------------------------------------------- /utilities/_visibility.scss: -------------------------------------------------------------------------------- 1 | @each $value in (hidden, visible) { 2 | @include make-utility(( 3 | visibility: $value 4 | )); 5 | } 6 | -------------------------------------------------------------------------------- /utilities/_width.scss: -------------------------------------------------------------------------------- 1 | @include make-utility(( 2 | alias: 'width-100' + $global-unit-percent, 3 | class: true, 4 | width: 100% 5 | )); 6 | 7 | @include make-utility(( 8 | width: 1px 9 | )); 10 | -------------------------------------------------------------------------------- /utilities/_z-index.scss: -------------------------------------------------------------------------------- 1 | @for $i from -1 through 9 { 2 | @include make-utility(( 3 | alias: 'z-index-' + $i, 4 | z-index: $i 5 | )); 6 | } 7 | --------------------------------------------------------------------------------