Skip to content

Commit

Permalink
feat: add utilities from bootstrap
Browse files Browse the repository at this point in the history
ref #189
  • Loading branch information
hjalmers committed Sep 3, 2021
1 parent ffa5074 commit 9e0c166
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 62 deletions.
163 changes: 163 additions & 0 deletions libs/chlorophyll/src/lib/common/functions/_bootstrap-functions.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
@use 'sass:math';
@use '../../tokens/config';

// Bootstrap functions
//
// Utility mixins and functions for evaluating source code across our variables, maps, and mixins.

// Ascending
// Used to evaluate Sass maps like our grid breakpoints.
@mixin assert-ascending($map, $map-name) {
$prev-key: null;
$prev-num: null;
@each $key, $num in $map {
@if $prev-num == null or unit($num) == '%' or unit($prev-num) == '%' {
// Do nothing
} @else if not comparable($prev-num, $num) {
@warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
} @else if $prev-num >= $num {
@warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
}
$prev-key: $key;
$prev-num: $num;
}
}

// Starts at zero
// Used to ensure the min-width of the lowest breakpoint starts at 0.
@mixin assert-starts-at-zero($map, $map-name: '$grid-breakpoints') {
@if length($map) > 0 {
$values: map-values($map);
$first-value: nth($values, 1);
@if $first-value != 0 {
@warn 'First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}.';
}
}
}

// Add important rule
@function is-important($important) {
@return #{if($important, '!important', '')};
}

// Replace `$search` with `$replace` in `$string`
// Used on our SVG icon backgrounds for custom forms.
//
// @author Hugo Giraudel
// @param {String} $string - Initial string
// @param {String} $search - Substring to replace
// @param {String} $replace ('') - New value
// @return {String} - Updated string
@function str-replace($string, $search, $replace: '') {
$index: str-index($string, $search);

@if $index {
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
}

@return $string;
}

// Internal Bootstrap function to turn maps into its negative variant.
// It prefixes the keys with `n` and makes the value negative.
@function negativify-map($map) {
$result: ();
@each $key, $value in $map {
@if $key != 0 {
$result: map-merge($result, ("n" + $key: (-$value)));
}
}
@return $result;
}
$enable-important-utilities: true;

// Utility generator
// Used to generate utilities & print utilities
@mixin generate-utility($utility, $infix, $is-rfs-media-query: false) {
$values: map-get($utility, values);

// If the values are a list or string, convert it into a map
@if type-of($values) == "string" or type-of(nth($values, 1)) != "list" {
$values: zip($values, $values);
}

@each $key, $value in $values {
$properties: map-get($utility, property);

// Multiple properties are possible, for example with vertical or horizontal margins or paddings
@if type-of($properties) == "string" {
$properties: append((), $properties);
}

// Use custom class if present
$property-class: if(map-has-key($utility, class), map-get($utility, class), nth($properties, 1));
$property-class: if($property-class == null, "", $property-class);

// State params to generate pseudo-classes
$state: if(map-has-key($utility, state), map-get($utility, state), ());

$infix: if($property-class == "" and str-slice($infix, 1, 1) == "-", str-slice($infix, 2), $infix);

// Don't prefix if value key is null (eg. with shadow class)
$property-class-modifier: if($key, if($property-class == "" and $infix == "", "", "-") + $key, "");

// Disable RFS for now
/*@if map-get($utility, rfs) {
// Inside the media query
@if $is-rfs-media-query {
$val: rfs-value($value);
// Do not render anything if fluid and non fluid values are the same
$value: if($val == rfs-fluid-value($value), null, $val);
}
@else {
$value: rfs-fluid-value($value);
}
}*/

$is-css-var: map-get($utility, css-var);
$is-local-vars: map-get($utility, local-vars);
$is-rtl: map-get($utility, rtl);

@if $value != null {
@if $is-rtl == false {
/* rtl:begin:remove */
}

@if $is-css-var {
.#{$property-class + $infix + $property-class-modifier} {
--#{config.$variable-prefix}#{$property-class}: #{$value};
}

@each $pseudo in $state {
.#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {
--#{config.$variable-prefix}#{$property-class}: #{$value};
}
}
} @else {
.#{$property-class + $infix + $property-class-modifier} {
@each $property in $properties {
@if $is-local-vars {
@each $local-var, $value in $is-local-vars {
--#{config.$variable-prefix}#{$local-var}: #{$value};
}
}
#{$property}: $value if($enable-important-utilities, !important, null);
}
}

@each $pseudo in $state {
.#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {
@each $property in $properties {
#{$property}: $value if($enable-important-utilities, !important, null);
}
}
}
}

@if $is-rtl == false {
/* rtl:end:remove */
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@use 'sass:math';
@use '../tokens/color' as tokens;
@use '../../tokens/color' as colors;

// Precomputed linear color channel values, for use in contrast calculations.
// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
Expand Down Expand Up @@ -199,8 +199,8 @@ $linear-channel-values: 0 0.0003035269835488375 0.000607053967097675
// .sample { light-or-dark: "light"; }
//
@function light-or-dark($color) {
$light-contrast: color-contrast($color, tokens.$white);
$dark-contrast: color-contrast($color, tokens.$black);
$light-contrast: color-contrast($color, colors.$white);
$dark-contrast: color-contrast($color, colors.$black);

@if $light-contrast > $dark-contrast {
// Contrast against white is higher than against black, so, this is a dark color
Expand All @@ -226,9 +226,9 @@ $linear-channel-values: 0 0.0003035269835488375 0.000607053967097675
$color-lod: light-or-dark($color);

@if ($color-lod == 'dark') {
@return tokens.$white;
@return colors.$white;
} @else {
@return tokens.$black;
@return colors.$black;
}
}

Expand Down Expand Up @@ -326,67 +326,11 @@ $linear-channel-values: 0 0.0003035269835488375 0.000607053967097675
}
}

@function max-contrast($fg, $bg: tokens.$background-color, $max: map-get(tokens.$disabled-colors, 'max')) {
@function max-contrast($fg, $bg: colors.$background-color, $max: map-get(colors.$disabled-colors, 'max')) {
@if (color-contrast($fg, $bg) > 3) {
@return $max;
} @else {
@return $fg;
}
}

// Bootstrap functions
//
// Utility mixins and functions for evaluating source code across our variables, maps, and mixins.

// Ascending
// Used to evaluate Sass maps like our grid breakpoints.
@mixin assert-ascending($map, $map-name) {
$prev-key: null;
$prev-num: null;
@each $key, $num in $map {
@if $prev-num == null or unit($num) == '%' or unit($prev-num) == '%' {
// Do nothing
} @else if not comparable($prev-num, $num) {
@warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
} @else if $prev-num >= $num {
@warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
}
$prev-key: $key;
$prev-num: $num;
}
}

// Starts at zero
// Used to ensure the min-width of the lowest breakpoint starts at 0.
@mixin assert-starts-at-zero($map, $map-name: '$grid-breakpoints') {
@if length($map) > 0 {
$values: map-values($map);
$first-value: nth($values, 1);
@if $first-value != 0 {
@warn 'First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}.';
}
}
}

// Add important rule
@function is-important($important) {
@return #{if($important, '!important', '')};
}

// Replace `$search` with `$replace` in `$string`
// Used on our SVG icon backgrounds for custom forms.
//
// @author Hugo Giraudel
// @param {String} $string - Initial string
// @param {String} $search - Substring to replace
// @param {String} $replace ('') - New value
// @return {String} - Updated string
@function str-replace($string, $search, $replace: '') {
$index: str-index($string, $search);

@if $index {
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
}

@return $string;
}
2 changes: 2 additions & 0 deletions libs/chlorophyll/src/lib/common/functions/_index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@forward "functions";
@forward "bootstrap-functions";
54 changes: 54 additions & 0 deletions libs/chlorophyll/src/lib/components/utility/_api.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// https://github.com/twbs/bootstrap/blob/main/scss/utilities/_api.scss
@use '../../tokens';
@use '../../tokens/utilities';
@use '../../common';
@use '../../common/functions';

// Loop over each breakpoint
@each $breakpoint in map-keys(tokens.$grid-breakpoints) {

// Generate media query if needed
@include common.media-breakpoint-up($breakpoint) {
$infix: common.breakpoint-infix($breakpoint, tokens.$grid-breakpoints);

// Loop over each utility property
@each $key, $utility in utilities.$utilities {
// The utility can be disabled with `false`, thus check if the utility is a map first
// Only proceed if responsive media queries are enabled or if it's the base media query
@if type-of($utility) == "map" and (map-get($utility, responsive) or $infix == "") {
@include functions.generate-utility($utility, $infix);
}
}
}
}

// Disable RFS for now
// RFS rescaling
/*@media (min-width: $rfs-mq-value) {
@each $breakpoint in map-keys(tokens.$grid-breakpoints) {
$infix: common.breakpoint-infix($breakpoint, tokens.$grid-breakpoints);
@if (map-get(tokens.$grid-breakpoints, $breakpoint) < $rfs-breakpoint) {
// Loop over each utility property
@each $key, $utility in $utilities {
// The utility can be disabled with `false`, thus check if the utility is a map first
// Only proceed if responsive media queries are enabled or if it's the base media query
@if type-of($utility) == "map" and map-get($utility, rfs) and (map-get($utility, responsive) or $infix == "") {
@include functions.generate-utility($utility, $infix, true);
}
}
}
}
}*/


// Print utilities
@media print {
@each $key, $utility in utilities.$utilities {
// The utility can be disabled with `false`, thus check if the utility is a map first
// Then check if the utility needs print styles
@if type-of($utility) == "map" and map-get($utility, print) == true {
@include functions.generate-utility($utility, "-print");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// https://github.com/twbs/bootstrap/blob/main/scss/bootstrap-utilities.scss
/*!
* Bootstrap Utilities v5.1.0 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/

// Configuration
//@import "functions";
//@import "variables";
//@import "mixins";
@use "../../tokens/utilities";

// Helpers
//@import "helpers";

// Utilities
//@import "utilities/api";
@use "api";
1 change: 1 addition & 0 deletions libs/chlorophyll/src/lib/components/utility/_index.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
@use 'spacing';
@use 'colors';
@use 'bootstrap-utilities';

0 comments on commit 9e0c166

Please sign in to comment.