diff --git a/src/addons/block/onlineusers/components/onlineusers/onlineusers.scss b/src/addons/block/onlineusers/components/onlineusers/onlineusers.scss
index c34c11af4..9c3d9a059 100644
--- a/src/addons/block/onlineusers/components/onlineusers/onlineusers.scss
+++ b/src/addons/block/onlineusers/components/onlineusers/onlineusers.scss
@@ -1,3 +1,5 @@
+@import "~theme/globals";
+
:host .core-block-content ::ng-deep {
max-height: 200px;
overflow-y: auto;
@@ -17,14 +19,13 @@
list-style-type: none;
.user {
- float: left;
+ @include float(start);
position: relative;
padding-bottom: 16px;
.core-adapted-img-container {
display: inline;
- margin-left: 0;
- margin-right: 8px;
+ @include margin-horizontal(0px, 8px);
}
.userpicture {
@@ -33,7 +34,7 @@
}
.message {
- float: right;
+ @include float(end);
margin-top: 3px;
}
@@ -48,20 +49,3 @@
}
}
-
-:host-context([dir=rtl]) .core-block-content ::ng-deep {
- .list li.listentry {
- .user {
- float: right;
-
- .core-adapted-img-container {
- margin-left: 8px;
- margin-right: 0;
- }
- }
-
- .message {
- float: left;
- }
- }
-}
diff --git a/src/addons/block/recentactivity/components/recentactivity/recentactivity.scss b/src/addons/block/recentactivity/components/recentactivity/recentactivity.scss
index a2e9d77a2..a72a303fb 100644
--- a/src/addons/block/recentactivity/components/recentactivity/recentactivity.scss
+++ b/src/addons/block/recentactivity/components/recentactivity/recentactivity.scss
@@ -1,3 +1,5 @@
+@import "~theme/globals";
+
:host .core-block-content ::ng-deep {
.activitydate, .activityhead {
text-align: center;
@@ -12,14 +14,8 @@
margin-bottom: 1em;
.head .date {
- float: right;
+ @include float(end);
}
}
}
}
-
-:host-context([dir=rtl]) .core-block-content ::ng-deep {
- .unlist li .head .date {
- float: left;
- }
-}
diff --git a/src/addons/calendar/components/calendar/calendar.scss b/src/addons/calendar/components/calendar/calendar.scss
index 5e8f8188d..b5c40fd10 100644
--- a/src/addons/calendar/components/calendar/calendar.scss
+++ b/src/addons/calendar/components/calendar/calendar.scss
@@ -22,17 +22,17 @@
.addon-calendar-day {
border-bottom: 1px solid var(--addon-calendar-border-color);
- border-right: 1px solid var(--addon-calendar-border-color);
+ @include border-end(1px, solid var(--addon-calendar-border-color));
overflow: hidden;
min-height: 60px;
cursor: pointer;
&:first-child {
- padding-left: 10px;
+ @include padding-horizontal(10px, null);
}
&:last-child {
- border-right: 0;
- padding-left: 8px;
+ @include border-end(0);
+ @include padding-horizontal(8px, null);
}
&.addon-calendar-event-past-day > .addon-calendar-dot-types,
@@ -89,9 +89,7 @@
}
.addon-calendar-day-more {
- margin-top: 0.6em;
- margin-bottom: 0.6em;
- margin-right: 4px;
+ @include margin(0.6em, null, 0.6em, 4px);
}
.addon-calendar-dot-types {
@@ -117,10 +115,10 @@
}
.addon-calendar-day-events {
- text-align: left;
+ text-align: start;
ion-icon {
- margin-right: 2px;
+ @include margin-horizontal(null, 2px);
font-size: 1em;
}
}
@@ -154,37 +152,6 @@
}
}
-:host-context([dir=rtl]) {
- .addon-calendar-day-events {
- text-align: right;
-
- ion-icon {
- margin-right: unset;
- margin-left: 2px;
- }
- }
-
- .addon-calendar-day {
- border-left: 1px solid var(--addon-calendar-border-color);
- border-right: unset;
-
- &:first-child {
- padding-right: 10px;
- padding-left: unset;
- }
- &:last-child {
- border-left: 0;
- border-right: unset;
- padding-right: 8px;
- padding-left: unset;
- }
- .addon-calendar-day-more {
- margin-left: 4px;
- margin-right: unset;
- }
- }
-}
-
:host-context(body.dark) {
--addon-calendar-blank-day-background-color: var(--gray-900);
}
diff --git a/src/addons/calendar/components/filter/filter.scss b/src/addons/calendar/components/filter/filter.scss
index 904ccbfab..29e652dd0 100644
--- a/src/addons/calendar/components/filter/filter.scss
+++ b/src/addons/calendar/components/filter/filter.scss
@@ -1,7 +1,9 @@
+@import "~theme/globals";
+
:host {
ion-item {
ion-icon, ion-radio {
- margin-right: 8px;
+ @include margin-horizontal(null, 8px);
}
> ion-icon {
@@ -10,12 +12,3 @@
}
}
}
-
-:host-context([dir=rtl]) {
- ion-item {
- ion-icon, ion-radio {
- margin-left: 8px;
- margin-right: unset;
- }
- }
-}
diff --git a/src/addons/messages/messages-common.scss b/src/addons/messages/messages-common.scss
index 8b480f462..255a9a156 100644
--- a/src/addons/messages/messages-common.scss
+++ b/src/addons/messages/messages-common.scss
@@ -1,3 +1,5 @@
+@import "~theme/globals";
+
:host {
.addon-messages-conversation-item,
.addon-message-discussion {
@@ -12,7 +14,7 @@
}
ion-icon {
- margin-left: 2px;
+ @include margin-horizontal(2px, null);
}
}
@@ -20,7 +22,7 @@
display: flex;
flex-direction: column;
align-self: flex-start;
- margin-left: 2px;
+ @include margin-horizontal(2px, null);
ion-badge, ion-icon {
margin-top: 3px;
@@ -40,7 +42,7 @@
.addon-message-last-message-user {
white-space: nowrap;
color: var(--ion-text-color);
- margin-right: 2px;
+ @include margin-horizontal(null, 2px);
}
.addon-message-last-message-text {
@@ -62,18 +64,3 @@
margin-right: 16px;
}
}
-
-:host-context([dir=rtl]) {
- .addon-messages-conversation-item,
- .addon-message-discussion {
- .item-heading ion-icon {
- margin-right: 2px;
- margin-left: 0;
- }
-
- .addon-message-last-message-user {
- margin-left: 2px;
- margin-right: 0;
- }
- }
-}
diff --git a/src/core/components/horizontal-scroll-controls/core-horizontal-scroll-controls.html b/src/core/components/horizontal-scroll-controls/core-horizontal-scroll-controls.html
index 787cea97b..b49f2c683 100644
--- a/src/core/components/horizontal-scroll-controls/core-horizontal-scroll-controls.html
+++ b/src/core/components/horizontal-scroll-controls/core-horizontal-scroll-controls.html
@@ -1,9 +1,9 @@
-
+
-
+
diff --git a/src/core/components/loading/loading.scss b/src/core/components/loading/loading.scss
index fc4d95b1f..efa86a0de 100644
--- a/src/core/components/loading/loading.scss
+++ b/src/core/components/loading/loading.scss
@@ -8,6 +8,7 @@
--loading-inline-margin: 0px;
--loading-inline-min-height: 28px;
--internal-loading-inline-min-height: var(--loading-inline-min-height);
+ --content-display: contents;
position: static;
color: var(--loading-text-color);
@@ -45,6 +46,7 @@
.core-loading-content {
@include core-transition(opacity, 200ms);
+ display: var(--content-display);
}
.core-loading-message {
@@ -75,10 +77,13 @@
height: 100%;
}
- &.has-spacer .core-loading-content {
- min-height: 100%;
- display: flex;
- flex-direction: column;
+ &.has-spacer {
+ --content-display: flex;
+
+ .core-loading-content {
+ min-height: 100%;
+ flex-direction: column;
+ }
}
&.list-item-limited-width .core-loading-content {
diff --git a/src/core/features/courses/components/course-list-item/course-list-item.scss b/src/core/features/courses/components/course-list-item/course-list-item.scss
index 48e3c7284..7d1bb34dc 100644
--- a/src/core/features/courses/components/course-list-item/course-list-item.scss
+++ b/src/core/features/courses/components/course-list-item/course-list-item.scss
@@ -35,7 +35,7 @@
// Common styles.
.item-heading ion-icon {
- margin-right: 4px;
+ @include margin-horizontal(null, 4px);
color: var(--core-star-color);
}
@@ -130,7 +130,7 @@ ion-card.core-course-list-item {
}
.core-course-maininfo {
- margin-right: 32px;
+ @include margin-horizontal(null, 32px);
}
}
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, "