diff --git a/src/core/features/login/pages/site/site.scss b/src/core/features/login/pages/site/site.scss index 927315fa1..b7e1f5767 100644 --- a/src/core/features/login/pages/site/site.scss +++ b/src/core/features/login/pages/site/site.scss @@ -38,9 +38,8 @@ max-width: fit-content; width: auto; height: auto; - margin: 0 auto; - margin-left: 50%; - transform: translateX(-50%); + margin: 0 50% 0 auto; // LTR will be applied on transform. + @include transform(translate3d(-50%,0,0)); object-fit: cover; object-position: 50% 50%; } diff --git a/src/theme/components/collapsible-item.scss b/src/theme/components/collapsible-item.scss index 3aad02e3e..33d010fda 100644 --- a/src/theme/components/collapsible-item.scss +++ b/src/theme/components/collapsible-item.scss @@ -53,10 +53,10 @@ @include core-transition(transform, 500ms); - @include push-arrow-color(626262, true); + @include push-arrow-color(#626262); @include darkmode() { - @include push-arrow-color(ffffff, true); + @include push-arrow-color(#ffffff); } } } diff --git a/src/theme/globals.mixins.ionic.scss b/src/theme/globals.mixins.ionic.scss deleted file mode 100644 index 3e9421a1b..000000000 --- a/src/theme/globals.mixins.ionic.scss +++ /dev/null @@ -1,629 +0,0 @@ -/* - * Imported ionic mixins for SCSS - * ---------------------------------------------------------------------------- - * Place here our custom mixins. - * Extracted from ionic.mixins.scss - * https://github.com/ionic-team/ionic-framework/blob/master/core/src/themes/ionic.mixins.scss - */ - -// Responsive Mixins -// -------------------------------------------------- -// Creates a fixed width for the grid based on the screen size -// --------------------------------------------------------------------------------- -@mixin make-grid-widths($widths: $grid-widths, $breakpoints: $screen-breakpoints) { - @each $breakpoint, $width in $widths { - @include media-breakpoint-up($breakpoint, $breakpoints) { - width: $width; - } - } - - max-width: 100%; -} - -// Adds padding to the element based on breakpoints -// --------------------------------------------------------------------------------- -@mixin make-breakpoint-padding($paddings) { - @each $breakpoint in map-keys($paddings) { - @include media-breakpoint-up($breakpoint) { - $padding: map-get($paddings, $breakpoint); - - @include padding($padding); - } - } -} - -// Gets the active color's css variable from a variation. Alpha is optional. -// -------------------------------------------------------------------------------------------- -// Example usage: -// current-color(base) => var(--ion-color-base) -// current-color(contrast, 0.1) => rgba(var(--ion-color-contrast-rgb), 0.1) -// -------------------------------------------------------------------------------------------- -@function current-color($variation, $alpha: null) { - @if $alpha == null { - @return var(--ion-color-#{$variation}); - } @else { - @return rgba(var(--ion-color-#{$variation}-rgb), #{$alpha}); - } -} - -// Mixes a color with black to create its shade. -// Default to bootstrap level 6. -// -------------------------------------------------------------------------------------------- -@function get-color-shade($color, $percent: 48%) { - @return mix(#000, $color, $percent); -} - -// Mixes a color with white to create its tint. -// Default to bootstrap level -10. -// -------------------------------------------------------------------------------------------- -@function get-color-tint($color, $percent: 80%) { - @return mix(#fff, $color, $percent); -} - -// Converts a color to a comma separated rgb. -// -------------------------------------------------------------------------------------------- -@function color-to-rgb-list($color) { - @return #{red($color)},#{green($color)},#{blue($color)}; -} - - -// Ionic Colors -// -------------------------------------------------- -// Generates the color classes and variables based on the -// colors map - -@mixin generate-color($color-name, $colors) { - $base: map-get($colors, $color-name); - $light: map-get($base, 'light'); - - @include generate-color-variants($color-name, $light); - - body.dark { - $dark: map-get($base, 'dark'); - $dark: mix($light, white, 80%) !default; - @include generate-color-variants($color-name, $dark); - } -} - -@mixin generate-color-variants($color-name, $base) { - $contrast: get_contrast_color($base); - $shade: get-color-shade($base); - $tint: get-color-tint($base); - - --#{$color-name}: #{$base}; - --#{$color-name}-shade: #{$shade}; - --#{$color-name}-tint: #{$tint}; - --#{$color-name}-contrast: #{$contrast}; - - // Internal ionic use only. - --ion-color-#{$color-name}: var(--#{$color-name}); - --ion-color-#{$color-name}-base: var(--ion-color-#{$color-name}); - --ion-color-#{$color-name}-rgb: #{color-to-rgb-list($base)}; - --ion-color-#{$color-name}-contrast: #{$contrast}; - --ion-color-#{$color-name}-contrast-rgb: #{color-to-rgb-list($contrast)}; - --ion-color-#{$color-name}-shade: #{$shade}; - --ion-color-#{$color-name}-tint: #{$tint}; - - .ion-color-#{$color-name} { - --ion-color: var(--ion-color-#{$color-name}); - --ion-color-base: var(--ion-color-#{$color-name}-base); - --ion-color-rgb: var(--ion-color-#{$color-name}-rgb); - --ion-color-contrast: var(--ion-color-#{$color-name}-contrast); - --ion-color-contrast-rgb: var(--ion-color-#{$color-name}-contrast-rgb); - --ion-color-shade: var(--ion-color-#{$color-name}-shade); - --ion-color-tint: var(--ion-color-#{$color-name}-tint); - } -} - -@mixin input-cover() { - @include position(0, null, null, 0); - @include margin(0); - - position: absolute; - - width: 100%; - height: 100%; - - border: 0; - background: transparent; - cursor: pointer; - - appearance: none; - outline: none; - - &::-moz-focus-inner { - border: 0; - } -} - -@mixin text-inherit() { - font-family: inherit; - font-size: inherit; - font-style: inherit; - font-weight: inherit; - letter-spacing: inherit; - text-decoration: inherit; - text-indent: inherit; - text-overflow: inherit; - text-transform: inherit; - text-align: inherit; - white-space: inherit; - color: inherit; -} - -@mixin button-state() { - @include position(0, 0, 0, 0); - - position: absolute; - - content: ""; - - opacity: 0; -} - -// Font smoothing -// -------------------------------------------------- - -@mixin font-smoothing() { - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; -} - -// Get the key from a map based on the index -@function index-to-key($map, $index) { - $keys: map-keys($map); - - @return nth($keys, $index); -} - - -// Breakpoint Mixins -// --------------------------------------------------------------------------------- - -// Breakpoint viewport sizes and media queries. -// -// Breakpoints are defined as a map of (name: minimum width), order from small to large: -// -// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px) -// -// The map defined in the `$screen-breakpoints` global variable is used as the `$breakpoints` argument by default. - -// --------------------------------------------------------------------------------- - -// Minimum breakpoint width. Null for the smallest (first) breakpoint. -// -// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) -// 576px -@function breakpoint-min($name, $breakpoints: $screen-breakpoints) { - $min: map-get($breakpoints, $name); - - @return if($name != index-to-key($breakpoints, 1), $min, null); -} - -// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront. -// Useful for making responsive utilities. -// -// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) -// "" (Returns a blank string) -// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) -// "-sm" -@function breakpoint-infix($name, $breakpoints: $screen-breakpoints) { - @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}"); -} - -// Media of at least the minimum breakpoint width. No query for the smallest breakpoint. -// Makes the @content apply to the given breakpoint and wider. -@mixin media-breakpoint-up($name, $breakpoints: $screen-breakpoints) { - $min: breakpoint-min($name, $breakpoints); - @if $min { - @media (min-width: $min) { - @content; - } - } @else { - @content; - } -} - -// Name of the next breakpoint, or null for the last breakpoint. -// -// >> breakpoint-next(sm) -// md -// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) -// md -// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl)) -// md -@function breakpoint-next($name, $breakpoints: $screen-breakpoints, $breakpoint-names: map-keys($breakpoints)) { - $n: index($breakpoint-names, $name); - @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null); -} - -// Maximum breakpoint width. Null for the smallest (first) breakpoint. -// The maximum value is reduced by 0.02px to work around the limitations of -// `min-` and `max-` prefixes and viewports with fractional widths. -// -// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max -// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari. // Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari. -// See https://bugs.webkit.org/show_bug.cgi?id=178261 // See https://bugs.webkit.org/show_bug.cgi?id=178261 -// -// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) -// 767.98px -@function breakpoint-max($name, $breakpoints: $screen-breakpoints) { - $max: map-get($breakpoints, $name); - @return if($max and $max > 0, $max - .02, null); -} - -// Media of at most the maximum breakpoint width. No query for the largest breakpoint. -// Makes the @content apply to the given breakpoint and narrower. -@mixin media-breakpoint-down($name, $breakpoints: $screen-breakpoints) { - $max: breakpoint-max($name, $breakpoints); - @if $max { - @media (max-width: $max) { - @content; - } - } @else { - @content; - } -} - - -// Text Direction - ltr / rtl -// -// CSS defaults to use the ltr css, and adds [dir=rtl] selectors -// to override ltr defaults. -// ---------------------------------------------------------- - -@mixin multi-dir() { - @content; - - // $root: #{&}; - // @at-root [dir] { - // #{$root} { - // @content; - // } - // } -} - -@mixin rtl() { - $root: #{&}; - - @at-root [dir=rtl] { - #{$root} { - @content; - } - } -} - -@mixin ltr() { - @content; -} - - -// SVG Background Image Mixin -// @param {string} $svg -// ---------------------------------------------------------- -@mixin svg-background-image($svg, $flip-rtl: false) { - $url: url-encode($svg); - $viewBox: str-split(str-extract($svg, "viewBox='", "'"), " "); - - @if $flip-rtl != true or $viewBox == null { - @include multi-dir() { - background-image: url("data:image/svg+xml;charset=utf-8,#{$url}"); - } - } @else { - $transform: "transform='translate(#{nth($viewBox, 3)}, 0) scale(-1, 1)'"; - $flipped-url: $svg; - $flipped-url: str-replace($flipped-url, ""; - @if $flip-rtl != true { - @include multi-dir() { - background-image: url("data:image/svg+xml;charset=utf-8,#{$svg}"); - } - } @else { - $flipped-svg: ""; +/** + * Same as item-push-svg-url but admits flip-rtl + */ +@mixin push-arrow-color($fill: 626262, $flip-rtl: false) { + $item-detail-push-svg: ""; - @include ltr () { - background-image: url("data:image/svg+xml;charset=utf-8,#{$svg}"); - } - @include rtl() { - background-image: url("data:image/svg+xml;charset=utf-8,#{$flipped-svg}"); - } - } + @include svg-background-image($item-detail-push-svg, $flip-rtl); } @mixin border-start($px, $type: null, $color: null) { - border-left: $px $type $color; - - @include rtl() { - border-left: unset; - border-right: $px $type $color; - } + @include property-horizontal(border, $px $type $color, null); } - @mixin border-end($px, $type: null, $color: null) { - border-right: $px $type $color; - - @include rtl() { - border-right: unset; - border-left: $px $type $color; - } + @include property-horizontal(border, null, $px $type $color); } @mixin safe-area-border-start($px, $type, $color) { @@ -167,41 +207,11 @@ } } -@mixin core-as-items() { - .item-md.item-block > .item-inner { - border-bottom: 1px solid $list-md-border-color; - } - - .item-ios.item-block > .item-inner { - border-bottom: $hairlines-width solid $list-ios-border-color; - } - - &:last-child .item > .item-inner { - border-bottom: 0; - } -} - -@mixin core-items() { - &.item-md.item-block > .item-inner { - border-bottom: 1px solid $list-md-border-color; - } - - &.item-ios.item-block > .item-inner { - border-bottom: $hairlines-width solid $list-ios-border-color; - } - - &.item-block:last-child > .item-inner { - border-bottom: 0; - } -} - @mixin darkmode() { $root: #{&}; - @at-root body.dark { - #{$root} { - @content; - } + @at-root #{add-root-selector($root, "body.dark")} { + @content; } } diff --git a/src/theme/helpers/helpers.scss b/src/theme/helpers/helpers.scss new file mode 100644 index 000000000..03d86944d --- /dev/null +++ b/src/theme/helpers/helpers.scss @@ -0,0 +1,15 @@ + +// Global Utility Functions +@import "./ionic.functions.string"; + +// Global Color Functions +@import "./ionic.functions.color"; + +// Global Mixins +@import "./ionic.mixins"; + +// Global Mixins +@import "./ionic.components.mixins"; + +// Custom Mixins +@import "./custom.mixins"; diff --git a/src/theme/helpers/ionic.components.mixins.scss b/src/theme/helpers/ionic.components.mixins.scss new file mode 100644 index 000000000..529dfb75a --- /dev/null +++ b/src/theme/helpers/ionic.components.mixins.scss @@ -0,0 +1,48 @@ +/** + * Imported ionic mixins for SCSS from different components + * ---------------------------------------------------------------------------- + * Extracted from + * https://github.com/ionic-team/ionic-framework/blob/master/core/src/components/grid/grid.mixins.scss + * https://github.com/ionic-team/ionic-framework/blob/master/core/src/components/item/item.mixins.scss + */ + +// Responsive Mixins +// -------------------------------------------------- + + +// Creates a fixed width for the grid based on the screen size +// --------------------------------------------------------------------------------- + +@mixin make-grid-widths($widths: $grid-widths, $breakpoints: $screen-breakpoints) { + @each $breakpoint, $width in $widths { + @include media-breakpoint-up($breakpoint, $breakpoints) { + width: $width; + } + } + + max-width: 100%; +} + + +// Adds padding to the element based on breakpoints +// --------------------------------------------------------------------------------- + +@mixin make-breakpoint-padding($paddings) { + @each $breakpoint in map-keys($paddings) { + @include media-breakpoint-up($breakpoint) { + $padding: map-get($paddings, $breakpoint); + + @include padding($padding); + } + } +} + + +// Item Mixins +// -------------------------------------------------- + +@mixin item-push-svg-url($fill) { + $item-detail-push-svg: ""; + + @include svg-background-image($item-detail-push-svg, true); +} diff --git a/src/theme/helpers/ionic.functions.color.scss b/src/theme/helpers/ionic.functions.color.scss new file mode 100644 index 000000000..a6b9060a7 --- /dev/null +++ b/src/theme/helpers/ionic.functions.color.scss @@ -0,0 +1,66 @@ +/** + * Imported ionic color functions for SCSS + * ---------------------------------------------------------------------------- + * Extracted from + * https://github.com/ionic-team/ionic-framework/blob/master/core/src/themes/ionic.functions.color.scss + */ + +// Gets the active color's css variable from a variation. Alpha is optional. +// -------------------------------------------------------------------------------------------- +// Example usage: +// current-color(base) => var(--ion-color-base) +// current-color(contrast, 0.1) => rgba(var(--ion-color-contrast-rgb), 0.1) +// -------------------------------------------------------------------------------------------- +@function current-color($variation, $alpha: null) { + @if $alpha == null { + @return var(--ion-color-#{$variation}); + } @else { + @return rgba(var(--ion-color-#{$variation}-rgb), #{$alpha}); + } +} + +// Gets the specific color's css variable from the name and variation. Alpha/rgb are optional. +// -------------------------------------------------------------------------------------------- +// Example usage: +// ion-color(primary, base) => var(--ion-color-primary, #3880ff) +// ion-color(secondary, contrast) => var(--ion-color-secondary-contrast) +// ion-color(primary, base, 0.5) => rgba(var(--ion-color-primary-rgb, 56, 128, 255), 0.5) +// -------------------------------------------------------------------------------------------- +@function ion-color($name, $variation, $alpha: null, $rgb: null) { + $values: map-get($colors, $name); + $value: map-get($values, $variation); + $variable: --ion-color-#{$name}-#{$variation}; + + @if ($variation == base) { + $variable: --ion-color-#{$name}; + } + + @if ($alpha) { + $value: color-to-rgb-list($value); + @return rgba(var(#{$variable}-rgb, $value), $alpha); + } + @if ($rgb) { + $value: color-to-rgb-list($value); + $variable: #{$variable}-rgb; + } + + @return var(#{$variable}, $value); +} + +// Mixes a color with black to create its shade. +// -------------------------------------------------------------------------------------------- +@function get-color-shade($color) { + @return mix(#000, $color, 12%); +} + +// Mixes a color with white to create its tint. +// -------------------------------------------------------------------------------------------- +@function get-color-tint($color) { + @return mix(#fff, $color, 10%); +} + +// Converts a color to a comma separated rgb. +// -------------------------------------------------------------------------------------------- +@function color-to-rgb-list($color) { + @return #{red($color)},#{green($color)},#{blue($color)}; +} diff --git a/src/theme/helpers/ionic.functions.string.scss b/src/theme/helpers/ionic.functions.string.scss new file mode 100644 index 000000000..db045dc75 --- /dev/null +++ b/src/theme/helpers/ionic.functions.string.scss @@ -0,0 +1,160 @@ +/** + * Imported ionic string functions for SCSS + * ---------------------------------------------------------------------------- + * Extracted from + * https://github.com/ionic-team/ionic-framework/blob/master/core/src/themes/ionic.functions.string.scss + */ + + +// String Utility Functions +// -------------------------------------------------------------------------------- + +// String Replace Function +// -------------------------------------------------------------------------------- + +@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; +} + +// String Split Function +// -------------------------------------------------------------------------------- + + +@function str-split($string, $separator) { + // empty array/list + $split-arr: (); + // first index of separator in string + $index: str-index($string, $separator); + // loop through string + @while $index != null { + // get the substring from the first character to the separator + $item: str-slice($string, 1, $index - 1); + // push item to array + $split-arr: append($split-arr, $item); + // remove item and separator from string + $string: str-slice($string, $index + 1); + // find new index of separator + $index: str-index($string, $separator); + } + // add the remaining string to list (the last item) + $split-arr: append($split-arr, $string); + + @return $split-arr; +} + + +// String Extract Function +// -------------------------------------------------------------------------------- + +@function str-extract($string, $start, $end) { + $start-index: str-index($string, $start); + + @if $start-index { + $post: str-slice($string, $start-index + str-length($start)); + $end-index: str-index($post, $end); + + @if $end-index { + @return str-slice($post, 1, $end-index - 1); + } + } + + @return null; +} + + +// String Contains Function +// -------------------------------------------------------------------------------- + +@function str-contains($string, $needle) { + @if (type-of($string) == string) { + @return str-index($string, $needle) != null; + } + + @return false; +} + + +// URL Encode Function +// -------------------------------------------------------------------------------- + +@function url-encode($val) { + $spaces: str-replace($val, " ", "%20"); + $encoded: str-replace($spaces, "#", "%23"); + @return $encoded; +} + + +// Add Root Selector +// -------------------------------------------------------------------------------- +// Adds a root selector using host-context based on the selector passed +// +// Examples +// -------------------------------------------------------------------------------- +// @include add-root-selector("[dir=rtl]", ":host") +// --> :host-context([dir=rtl]) +// +// @include add-root-selector("[dir=rtl]", ":host(.fixed)") +// --> :host-context([dir=rtl]):host(.fixed) +// --> :host-context([dir=rtl]).fixed +// +// @include add-root-selector("[dir=rtl]", ":host(.tab-layout-icon-hide) ::slotted(ion-badge)") +// --> :host-context([dir=rtl]).tab-layout-icon-hide ::slotted(ion-badge) +// +// @include add-root-selector("[dir=rtl]", ".shadow") +// --> [dir=rtl] .shadow +// --> :host-context([dir=rtl]) .shadow +// -------------------------------------------------------------------------------- + +@function add-root-selector($root, $addHostSelector) { + $selectors: str-split($root, ","); + + $list: (); + + @each $selector in $selectors { + // If the selector contains :host( it means it is targeting a class on the host + // element so we need to change how we target it + @if str-contains($selector, ":host(") { + $shadow-element: str-replace($selector, ":host(", ":host-context(#{$addHostSelector}):host("); + $list: append($list, $shadow-element, comma); + + $new-element: (); + $elements: str-split($selector, " "); + + @each $element in $elements { + @if str-contains($element, ":host(") { + $scoped-element: $element; + + @if str-contains($element, "))") { + $scoped-element: str-replace($scoped-element, "))", ")"); + } @else { + $scoped-element: str-replace($scoped-element, ")", ""); + } + $scoped-element: str-replace($scoped-element, ":host(", ":host-context(#{$addHostSelector})"); + + $new-element: append($new-element, $scoped-element, space); + } @else { + $new-element: append($new-element, $element, space); + } + } + + $list: append($list, $new-element, comma); + // If the selector contains :host it means it is targeting just the host + // element so we can change it to look for host-context + } @else if str-contains($selector, ":host") { + $list: append($list, ":host-context(#{$addHostSelector})", comma); + // If the selector does not contain host at all it is either a shadow + // or normal element so append both the dir check and host-context + } @else { + $list: append($list, "#{$addHostSelector} #{$selector}", comma); + $list: append($list, ":host-context(#{$addHostSelector}) #{$selector}", comma); + } + } + + @return $list; +} diff --git a/src/theme/helpers/ionic.mixins.scss b/src/theme/helpers/ionic.mixins.scss new file mode 100644 index 000000000..f212ad5f7 --- /dev/null +++ b/src/theme/helpers/ionic.mixins.scss @@ -0,0 +1,542 @@ +/** + * Imported ionic mixins for SCSS + * ---------------------------------------------------------------------------- + * Extracted from + * https://github.com/ionic-team/ionic-framework/blob/master/core/src/themes/ionic.mixins.scss + */ + +@mixin input-cover() { + @include position(0, null, null, 0); + @include margin(0); + + position: absolute; + + width: 100%; + height: 100%; + + border: 0; + background: transparent; + cursor: pointer; + + appearance: none; + outline: none; + + &::-moz-focus-inner { + border: 0; + } +} + +@mixin visually-hidden() { + position: absolute; + + top: 0; + left: 0; + right: 0; + bottom: 0; + + width: 100%; + height: 100%; + + margin: 0; + padding: 0; + + border: 0; + outline: 0; + clip: rect(0 0 0 0); + + opacity: 0; + overflow: hidden; + + -webkit-appearance: none; + -moz-appearance: none; +} + +@mixin text-inherit() { + font-family: inherit; + font-size: inherit; + font-style: inherit; + font-weight: inherit; + letter-spacing: inherit; + text-decoration: inherit; + text-indent: inherit; + text-overflow: inherit; + text-transform: inherit; + text-align: inherit; + white-space: inherit; + color: inherit; +} + +@mixin button-state() { + @include position(0, 0, 0, 0); + + position: absolute; + + content: ""; + + opacity: 0; +} + +// Font smoothing +// -------------------------------------------------- + +@mixin font-smoothing() { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; +} + +// Get the key from a map based on the index +@function index-to-key($map, $index) { + $keys: map-keys($map); + + @return nth($keys, $index); +} + + +// Breakpoint Mixins +// --------------------------------------------------------------------------------- + +// Breakpoint viewport sizes and media queries. +// +// Breakpoints are defined as a map of (name: minimum width), order from small to large: +// +// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px) +// +// The map defined in the `$screen-breakpoints` global variable is used as the `$breakpoints` argument by default. + +// --------------------------------------------------------------------------------- + +// Minimum breakpoint width. Null for the smallest (first) breakpoint. +// +// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) +// 576px +@function breakpoint-min($name, $breakpoints: $screen-breakpoints) { + $min: map-get($breakpoints, $name); + + @return if($name != index-to-key($breakpoints, 1), $min, null); +} + +// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront. +// Useful for making responsive utilities. +// +// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) +// "" (Returns a blank string) +// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) +// "-sm" +@function breakpoint-infix($name, $breakpoints: $screen-breakpoints) { + @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}"); +} + +// Media of at least the minimum breakpoint width. No query for the smallest breakpoint. +// Makes the @content apply to the given breakpoint and wider. +@mixin media-breakpoint-up($name, $breakpoints: $screen-breakpoints) { + $min: breakpoint-min($name, $breakpoints); + @if $min { + @media (min-width: $min) { + @content; + } + } @else { + @content; + } +} + +// Name of the next breakpoint, or null for the last breakpoint. +// +// >> breakpoint-next(sm) +// md +// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) +// md +// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl)) +// md +@function breakpoint-next($name, $breakpoints: $screen-breakpoints, $breakpoint-names: map-keys($breakpoints)) { + $n: index($breakpoint-names, $name); + @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null); +} + +// Maximum breakpoint width. Null for the smallest (first) breakpoint. +// The maximum value is reduced by 0.02px to work around the limitations of +// `min-` and `max-` prefixes and viewports with fractional widths. +// +// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max +// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari. // Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari. +// See https://bugs.webkit.org/show_bug.cgi?id=178261 // See https://bugs.webkit.org/show_bug.cgi?id=178261 +// +// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)) +// 767.98px +@function breakpoint-max($name, $breakpoints: $screen-breakpoints) { + $max: map-get($breakpoints, $name); + @return if($max and $max > 0, $max - .02, null); +} + +// Media of at most the maximum breakpoint width. No query for the largest breakpoint. +// Makes the @content apply to the given breakpoint and narrower. +@mixin media-breakpoint-down($name, $breakpoints: $screen-breakpoints) { + $max: breakpoint-max($name, $breakpoints); + @if $max { + @media (max-width: $max) { + @content; + } + } @else { + @content; + } +} + + +// Text Direction - ltr / rtl +// +// CSS defaults to use the ltr css, and adds [dir=rtl] selectors +// to override ltr defaults. +// ---------------------------------------------------------- + +@mixin multi-dir() { + @content; + + // $root: #{&}; + // @at-root [dir] { + // #{$root} { + // @content; + // } + // } +} + +@mixin rtl() { + $root: #{&}; + + @at-root #{add-root-selector($root, "[dir=rtl]")} { + @content; + } +} + +@mixin ltr() { + @content; +} + + +// SVG Background Image Mixin +// @param {string} $svg +// ---------------------------------------------------------- +@mixin svg-background-image($svg, $flip-rtl: false) { + $url: url-encode($svg); + $viewBox: str-split(str-extract($svg, "viewBox='", "'"), " "); + + @if $flip-rtl != true or $viewBox == null { + @include multi-dir() { + background-image: url("data:image/svg+xml;charset=utf-8,#{$url}"); + } + } @else { + $transform: "transform='translate(#{nth($viewBox, 3)}, 0) scale(-1, 1)'"; + $flipped-url: $svg; + $flipped-url: str-replace($flipped-url, "