diff --git a/packages/core/src/components/tabs/tab-bar.tsx b/packages/core/src/components/tabs/tab-bar.tsx
new file mode 100644
index 0000000000..ba1642439f
--- /dev/null
+++ b/packages/core/src/components/tabs/tab-bar.tsx
@@ -0,0 +1,55 @@
+import { Component, h } from '@stencil/core';
+import { VNodeData } from '../../utils/interfaces';
+
+
+
+@Component({
+ tag: 'ion-tab-bar',
+ host: {
+ theme: 'tabbar'
+ }
+})
+export class TabBar {
+ @Prop() tabs: [Tab] = [];
+
+ @Prop() onTabSelected: Function;
+
+ @Prop() selectedIndex: number = 0;
+
+ /**
+ * @prop {string} Set the tabbar layout: `icon-top`, `icon-start`, `icon-end`, `icon-bottom`, `icon-hide`, `title-hide`.
+ */
+ @Prop() tabsLayout: string = 'icon-top'
+
+ hostData(): VNodeData {
+ return {
+ attrs: {
+ 'role': 'tablist'
+ },
+ class: {
+ 'tabbar': true
+ }
+ }
+ }
+
+ handleTabButtonClick(tab, index) {
+ this.onTabSelected && this.onTabSelected(tab, index);
+ }
+
+ render() {
+ return (
+
+ {this.tabs.map((tab, index) => {
+ return (
+
+ )
+ })}
+
+ )
+ }
+}
diff --git a/packages/core/src/components/tabs/tab-button.tsx b/packages/core/src/components/tabs/tab-button.tsx
new file mode 100644
index 0000000000..4130bc7005
--- /dev/null
+++ b/packages/core/src/components/tabs/tab-button.tsx
@@ -0,0 +1,78 @@
+import { Component, h } from '@stencil/core';
+import { VNodeData } from '../../utils/interfaces';
+
+@Component({
+ tag: 'ion-tab-button',
+ host: {
+ theme: 'tab-button'
+ }
+})
+export class TabButton {
+ @Prop() tab: Tab;
+
+ @Prop() layout: string;
+
+ @Prop() selectedIndex: number;
+
+ @Prop() index: number;
+
+ hostData(): VNodeData {
+ const tab = this.tab;
+ if(!tab) return {};
+
+ // attr.id
+ // attr.aria-controls
+
+ const hasTitle = !!tab.tabTitle;
+ const hasIcon = !!tab.tabIcon && this.layout !== 'icon-hide';
+ const hasTitleOnly = (hasTitle && !hasIcon);
+ const hasIconOnly = (hasIcon && !hasTitle);
+ const hasBadge = !!tab.tabBadge;
+ // class.disable-hover
+ // class.tab-disabled
+ // class.tab-hidden
+
+ return {
+ attrs: {
+ 'aria-selected': this.selectedIndex == this.index
+ },
+ class: {
+ 'has-title': hasTitle,
+ 'has-icon': hasIcon,
+ 'has-title-only': hasTitleOnly,
+ 'has-icon-only': hasIconOnly,
+ 'has-badge': hasBadge
+ }
+ };
+ }
+
+ render() {
+ if(!this.tab) {
+ return null;
+ }
+
+ const tab = this.tab
+
+ // TODO: Apply these on host?
+ let { id, ariaControls, ariaSelected, hasTitle, hasIcon, hasTitleOnly,
+ iconOnly, hasBadge, disableHover, tabDisabled, tabHidden } = {};
+
+ const items = []
+
+ if(tab.tabIcon) {
+ items.push();
+ }
+
+ if(tab.tabTitle) {
+ items.push({tab.tabTitle});
+ }
+
+ if(tab.tabBadge) {
+ items.push({tab.tabBadge});
+ }
+
+ items.push();
+
+ return (items)
+ }
+}
diff --git a/packages/core/src/components/tabs/tab-highlight.tsx b/packages/core/src/components/tabs/tab-highlight.tsx
new file mode 100644
index 0000000000..fc09f7c209
--- /dev/null
+++ b/packages/core/src/components/tabs/tab-highlight.tsx
@@ -0,0 +1,13 @@
+import { Component, h } from '@stencil/core';
+
+
+@Component({
+ tag: 'ion-tab-highlight'
+})
+export class TabHighlight {
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/packages/core/src/components/tabs/tab.tsx b/packages/core/src/components/tabs/tab.tsx
new file mode 100644
index 0000000000..df600f8caa
--- /dev/null
+++ b/packages/core/src/components/tabs/tab.tsx
@@ -0,0 +1,110 @@
+import { Component, h, Ionic } from '@stencil/core';
+import { VNodeData } from '../../utils/interfaces';
+
+
+@Component({
+ tag: 'ion-tab',
+ host: {
+ theme: 'tab'
+ }
+})
+export class Tab {
+ /**
+ * @prop {Page} Set the root component for this tab.
+ */
+ @Prop() root: string;
+
+ /**
+ * @prop {object} Any nav-params to pass to the root componentof this tab.
+ */
+ @Prop() rootParams: any;
+
+ /**
+ * @prop {boolean} If true, the tab is selected
+ */
+ @State() isSelected: Boolean = false;
+
+ /**
+ * @prop {string} The title of the tab button.
+ */
+ @Prop() tabTitle: string;
+
+ /**
+ * @prop {string} The icon for the tab button.
+ */
+ @Prop() tabIcon: string;
+
+ /**
+ * @prop {string} The badge for the tab button.
+ */
+ @Prop() tabBadge: string;
+
+ /**
+ * @prop {string} The badge color for the tab button.
+ */
+ @Prop() tabBadgeStyle: string;
+
+ /**
+ * @prop {boolean} If true, enable the tab. If false,
+ * the user cannot interact with this element.
+ * Default: `true`.
+ */
+ @Prop() enabled: boolean = true;
+
+ /**
+ * @prop {boolean} If true, the tab button is visible within the
+ * tabbar. Default: `true`.
+ */
+ @Prop() shown: boolean = true;
+
+ /**
+ * @prop {boolean} If true, hide the tabs on child pages.
+ */
+ @Prop() tabsHideOnSubPages: boolean = false;
+
+ /**
+ * @prop {Tab} Emitted when the current tab is selected.
+ */
+ @Prop() onSelected: Function;
+
+ hostData(): VNodeData {
+ return {
+ style: {
+ display: !this.isSelected && 'none' || ''
+ },
+ attrs: {
+ 'role': 'tabpanel'
+ //'id': _tabId,
+ //aria-labelledby: _btnId
+ },
+ class: {
+ }
+ };
+ }
+
+ ionViewDidLoad() {
+ setTimeout(() => {
+ Ionic.emit(this, 'ionTabDidLoad', {
+ detail: {
+ tab: this
+ }
+ })
+ }, 0)
+ }
+
+ ionViewDidUnload() {
+ Ionic.emit(this, 'ionTabDidLoad', {
+ detail: {
+ tab: this
+ }
+ })
+ }
+
+ render() {
+ const RootComponent = this.root;
+ return [
+ ,
+
+ ]
+ }
+}
diff --git a/packages/core/src/components/tabs/tabs.ios.scss b/packages/core/src/components/tabs/tabs.ios.scss
new file mode 100644
index 0000000000..8fe133287b
--- /dev/null
+++ b/packages/core/src/components/tabs/tabs.ios.scss
@@ -0,0 +1,175 @@
+@import "../../themes/ionic.globals.ios";
+@import "./tabs";
+
+// iOS Tabs
+// --------------------------------------------------
+
+/// @prop - Border on the tabbar (border-top when [tabsPlacement=bottom] and border-bottom when [tabsPlacement=top])
+$tabs-ios-border: $hairlines-width solid $tabs-ios-border-color !default;
+
+// deprecated
+$tabs-ios-tab-padding: null !default;
+
+/// @prop - Padding top on the tab button
+$tabs-ios-tab-padding-top: 0 !default;
+
+/// @prop - Padding end on the tab button
+$tabs-ios-tab-padding-end: 2px !default;
+
+/// @prop - Padding bottom on the tab button
+$tabs-ios-tab-padding-bottom: $tabs-ios-tab-padding-top !default;
+
+/// @prop - Padding start on the tab button
+$tabs-ios-tab-padding-start: $tabs-ios-tab-padding-end !default;
+
+/// @prop - Max width of the tab button
+$tabs-ios-tab-max-width: 240px !default;
+
+/// @prop - Minimum height of the tab button
+$tabs-ios-tab-min-height: 49px !default;
+
+/// @prop - Text color of the inactive tab button
+$tabs-ios-tab-text-color: $tabs-ios-tab-color-inactive !default;
+
+/// @prop - Text color of the active tab button
+$tabs-ios-tab-text-color-active: $tabs-ios-tab-color-active !default;
+
+/// @prop - Icon color of the inactive tab button
+$tabs-ios-tab-icon-color: $tabs-ios-tab-color-inactive !default;
+
+/// @prop - Icon color of the active tab button
+$tabs-ios-tab-icon-color-active: $tabs-ios-tab-color-active !default;
+
+/// @prop - Font size of the tab button text
+$tabs-ios-tab-font-size: 10px !default;
+
+/// @prop - Size of the tab button icon
+$tabs-ios-tab-icon-size: 30px !default;
+
+
+.tabs-ios .tabbar {
+ justify-content: center;
+
+ border-top: $tabs-ios-border;
+ background: $tabs-ios-background;
+}
+
+.tabs-ios[tabsPlacement=top] .tabbar {
+ border-top: 0;
+ border-bottom: $tabs-ios-border;
+}
+
+.tabs-ios .tab-button {
+ max-width: $tabs-ios-tab-max-width;
+ min-height: $tabs-ios-tab-min-height;
+
+ font-size: $tabs-ios-tab-font-size;
+ color: $tabs-ios-tab-text-color;
+
+ @include deprecated-variable(padding, $tabs-ios-tab-padding) {
+ @include padding($tabs-ios-tab-padding-top, $tabs-ios-tab-padding-end, $tabs-ios-tab-padding-bottom, $tabs-ios-tab-padding-start);
+ }
+}
+
+.tabs-ios .tab-button:hover:not(.disable-hover),
+.tabs-ios .tab-button[aria-selected=true] {
+ color: $tabs-ios-tab-text-color-active;
+}
+
+.tabs-ios .tab-button[aria-selected=true] .tab-button-icon {
+ color: $tabs-ios-tab-icon-color-active;
+}
+
+.tabs-ios .tab-button-text {
+ @include margin(0, null, 1px, null);
+
+ min-height: $tabs-ios-tab-font-size + 1;
+}
+
+.tabs-ios .has-title-only .tab-button-text {
+ font-size: $tabs-ios-tab-font-size + 2;
+}
+
+.tabs-ios .tab-button-icon {
+ @include margin(4px, null, 1px, null);
+
+ min-width: $tabs-ios-tab-icon-size + 5;
+ height: $tabs-ios-tab-icon-size;
+
+ font-size: $tabs-ios-tab-icon-size;
+
+ color: $tabs-ios-tab-icon-color;
+}
+
+.tabs-ios .tab-button-icon::before {
+ vertical-align: top;
+}
+
+.tabs-ios[tabsLayout=icon-right] .tab-button .tab-button-text, // deprecated
+.tabs-ios[tabsLayout=icon-left] .tab-button .tab-button-text, // deprecated
+.tabs-ios[tabsLayout=icon-end] .tab-button .tab-button-text,
+.tabs-ios[tabsLayout=icon-start] .tab-button .tab-button-text {
+ font-size: 1.4rem;
+ line-height: 1.1;
+}
+
+.tabs-ios[tabsLayout=icon-right] .tab-button ion-icon, // deprecated
+.tabs-ios[tabsLayout=icon-left] .tab-button ion-icon, // deprecated
+.tabs-ios[tabsLayout=icon-end] .tab-button ion-icon,
+.tabs-ios[tabsLayout=icon-start] .tab-button ion-icon {
+ min-width: 24px;
+ height: 26px;
+
+ font-size: 24px;
+}
+
+.tabs-ios[tabsLayout=icon-hide] .tab-button,
+.tabs-ios .tab-button.has-title-only {
+ min-height: $tabs-ios-tab-min-height - 8;
+}
+
+.tabs-ios[tabsLayout=icon-hide] .tab-button .tab-button-text,
+.tabs-ios .tab-button.has-title-only .tab-button-text {
+ @include margin(2px, 0);
+
+ font-size: 1.4rem;
+ line-height: 1.1;
+}
+
+.tabs-ios[tabsLayout=title-hide] .tab-button,
+.tabs-ios .tab-button.icon-only {
+ min-height: $tabs-ios-tab-min-height - 8;
+}
+
+
+// iOS Tabbar Color Mixin
+// --------------------------------------------------
+
+@mixin tabbar-ios($color-name, $color-base, $color-contrast) {
+
+ .tabs-ios-#{$color-name} .tabbar {
+ border-color: darken($color-base, 10%);
+ background-color: $color-base;
+ }
+
+ .tabs-ios-#{$color-name} .tab-button,
+ .tabs-ios-#{$color-name} .tab-button-icon,
+ .tabs-ios-#{$color-name} .tab-button:hover:not(.disable-hover),
+ .tabs-ios-#{$color-name} .tab-button:hover:not(.disable-hover) .tab-button-icon {
+ color: rgba($color-contrast, .7);
+ }
+
+ .tabs-ios-#{$color-name} .tab-button[aria-selected=true],
+ .tabs-ios-#{$color-name} .tab-button[aria-selected=true] .tab-button-icon {
+ color: $color-contrast;
+ }
+
+}
+
+
+// iOS Tabbar Color Generation
+// --------------------------------------------------
+
+@each $color-name, $color-base, $color-contrast in get-colors($colors-ios) {
+ @include tabbar-ios($color-name, $color-base, $color-contrast);
+}
diff --git a/packages/core/src/components/tabs/tabs.md.scss b/packages/core/src/components/tabs/tabs.md.scss
new file mode 100644
index 0000000000..ba1d291ab9
--- /dev/null
+++ b/packages/core/src/components/tabs/tabs.md.scss
@@ -0,0 +1,344 @@
+@import "../../themes/ionic.globals.md";
+@import "./tabs";
+
+
+// Material Design Tabs
+// --------------------------------------------------
+
+// deprecated
+$tabs-md-tab-padding: null !default;
+
+/// @prop - Padding top on the tab button
+$tabs-md-tab-padding-top: 0 !default;
+
+/// @prop - Padding end on the tab button
+$tabs-md-tab-padding-end: $tabs-md-tab-padding-top !default;
+
+/// @prop - Padding bottom on the tab button
+$tabs-md-tab-padding-bottom: $tabs-md-tab-padding-top !default;
+
+/// @prop - Padding start on the tab button
+$tabs-md-tab-padding-start: $tabs-md-tab-padding-end !default;
+
+/// @prop - Minimum height of the tab button
+$tabs-md-tab-min-height: 5.6rem !default;
+
+/// @prop - Font size of the inactive tab button text
+$tabs-md-tab-font-size: 1.2rem !default;
+
+/// @prop - Font weight of the tab button text
+$tabs-md-tab-font-weight: normal !default;
+
+/// @prop - Opacity of the inactive tab button
+$tabs-md-tab-opacity: .7 !default;
+
+/// @prop - Text color of the inactive tab button
+$tabs-md-tab-text-color: rgba($tabs-md-tab-color-inactive, $tabs-md-tab-opacity) !default;
+
+/// @prop - Text color of the active tab button
+$tabs-md-tab-text-color-active: $tabs-md-tab-color-active !default;
+
+/// @prop - Icon color of the inactive tab button
+$tabs-md-tab-icon-color: rgba($tabs-md-tab-color-inactive, $tabs-md-tab-opacity) !default;
+
+/// @prop - Icon color of the active tab button
+$tabs-md-tab-icon-color-active: $tabs-md-tab-color-active !default;
+
+// deprecated
+$tabs-md-tab-padding-active: null !default;
+
+/// @prop - Padding top of the active tab button
+$tabs-md-tab-padding-active-top: 0 !default;
+
+/// @prop - Padding end of the active tab button
+$tabs-md-tab-padding-active-end: $tabs-md-tab-padding-active-top !default;
+
+/// @prop - Padding bottom of the active tab button
+$tabs-md-tab-padding-active-bottom: $tabs-md-tab-padding-active-top !default;
+
+/// @prop - Padding start of the active tab button
+$tabs-md-tab-padding-active-start: $tabs-md-tab-padding-active-end !default;
+
+/// @prop - Font size of the active tab button text
+$tabs-md-tab-font-size-active: 1.4rem !default;
+
+// deprecated
+$tabs-md-tab-text-margin: null !default;
+
+/// @prop - Margin top on the tab button text
+$tabs-md-tab-text-margin-top: 0 !default;
+
+/// @prop - Margin end on the tab button text
+$tabs-md-tab-text-margin-end: $tabs-md-tab-text-margin-top !default;
+
+/// @prop - Margin bottom on the tab button text
+$tabs-md-tab-text-margin-bottom: $tabs-md-tab-text-margin-top !default;
+
+/// @prop - Margin start on the tab button text
+$tabs-md-tab-text-margin-start: $tabs-md-tab-text-margin-end !default;
+
+/// @prop - Capitalization of the tab button text
+$tabs-md-tab-text-capitalization: none !default;
+
+// deprecated
+$tabs-md-tab-text-transform-origin: null !default;
+
+/// @prop - Transform origin x for the tab button text
+$tabs-md-tab-text-transform-origin-x: 50% !default;
+
+/// @prop - Transform origin y for the tab button text
+$tabs-md-tab-text-transform-origin-y: 80% !default;
+
+/// @prop - Transform for the active tab button text
+$tabs-md-tab-text-transform-active: scale3d($tabs-md-tab-font-size-active / $tabs-md-tab-font-size, $tabs-md-tab-font-size-active / $tabs-md-tab-font-size, 1) !default;
+
+/// @prop - Text transition for the tab button text
+$tabs-md-tab-text-transition: transform .3s ease-in-out !default;
+
+// deprecated
+$tabs-md-tab-icon-transform-active: null !default;
+
+/// @prop - Transform x for the active tab button icon when the layout is icon-top, icon-only, or title-only
+$tabs-md-tab-icon-transform-x-active: 0 !default;
+
+/// @prop - Transform y for the active tab button icon when the layout is icon-top, icon-only, or title-only
+$tabs-md-tab-icon-transform-y-active: -2px !default;
+
+/// @prop - Transform z for the active tab button icon when the layout is icon-top, icon-only, or title-only
+$tabs-md-tab-icon-transform-z-active: 0 !default;
+
+// deprecated
+$tabs-md-tab-icon-end-transform-active: null !default;
+
+/// @prop - Transform x for the active tab button icon when the layout is icon-right
+$tabs-md-tab-icon-right-transform-x-active: 2px !default;
+
+/// @prop - Transform y for the active tab button icon when the layout is icon-right
+$tabs-md-tab-icon-right-transform-y-active: 0 !default;
+
+/// @prop - Transform z for the active tab button icon when the layout is icon-right
+$tabs-md-tab-icon-right-transform-z-active: 0 !default;
+
+// deprecated
+$tabs-md-tab-icon-bottom-transform-active: null !default;
+
+/// @prop - Transform x for the active tab button icon when the layout is icon-bottom
+$tabs-md-tab-icon-bottom-transform-x-active: 0 !default;
+
+/// @prop - Transform y for the active tab button icon when the layout is icon-bottom
+$tabs-md-tab-icon-bottom-transform-y-active: 2px !default;
+
+/// @prop - Transform z for the active tab button icon when the layout is icon-bottom
+$tabs-md-tab-icon-bottom-transform-z-active: 0 !default;
+
+// deprecated
+$tabs-md-tab-icon-start-transform-active: null !default;
+
+/// @prop - Transform x for the active tab button icon when the layout is icon-left
+$tabs-md-tab-icon-left-transform-x-active: -2px !default;
+
+/// @prop - Transform y for the active tab button icon when the layout is icon-left
+$tabs-md-tab-icon-left-transform-y-active: 0 !default;
+
+/// @prop - Transform z for the active tab button icon when the layout is icon-left
+$tabs-md-tab-icon-left-transform-z-active: 0 !default;
+
+// deprecated
+$tabs-md-tab-icon-transform-origin: null !default;
+
+/// @prop - Transform origin x for the tab button text
+$tabs-md-tab-icon-transform-origin-x: 50% !default;
+
+/// @prop - Transform origin y for the tab button text
+$tabs-md-tab-icon-transform-origin-y: 150% !default;
+
+/// @prop - Text transition for the tab button icon
+$tabs-md-tab-icon-transition: transform .3s ease-in-out !default;
+
+/// @prop - Size of the tab button icon
+$tabs-md-tab-icon-size: 2.4rem !default;
+
+
+.tabs-md .tabbar {
+ background: $tabs-md-background;
+}
+
+
+// Material Design Tab Button
+// --------------------------------------------------
+
+.tabs-md .tab-button {
+ min-height: $tabs-md-tab-min-height;
+
+ font-weight: $tabs-md-tab-font-weight;
+ color: $tabs-md-tab-text-color;
+
+ @include deprecated-variable(padding, $tabs-md-tab-padding) {
+ @include padding($tabs-md-tab-padding-top, $tabs-md-tab-padding-end, $tabs-md-tab-padding-bottom, $tabs-md-tab-padding-start);
+ }
+}
+
+.tabs-md .tab-button[aria-selected=true] {
+ color: $tabs-md-tab-text-color-active;
+
+ @include deprecated-variable(padding, $tabs-md-tab-padding-active) {
+ @include padding($tabs-md-tab-padding-active-top, $tabs-md-tab-padding-active-end, $tabs-md-tab-padding-active-bottom, $tabs-md-tab-padding-active-start);
+ }
+}
+
+
+// Material Design Tab Button Text
+// --------------------------------------------------
+
+.tabs-md .tab-button-text {
+ font-size: $tabs-md-tab-font-size;
+
+ text-transform: $tabs-md-tab-text-capitalization;
+ transition: $tabs-md-tab-text-transition;
+
+ @include deprecated-variable(margin, $tabs-md-tab-text-margin) {
+ @include margin($tabs-md-tab-text-margin-top, $tabs-md-tab-text-margin-end, $tabs-md-tab-text-margin-bottom, $tabs-md-tab-text-margin-start);
+ }
+
+ @include deprecated-variable(transform-origin, $tabs-md-tab-text-transform-origin) {
+ @include transform-origin($tabs-md-tab-text-transform-origin-x, $tabs-md-tab-text-transform-origin-y);
+ }
+}
+
+.tabs-md .tab-button[aria-selected=true] .tab-button-text {
+ transform: $tabs-md-tab-text-transform-active;
+ transition: $tabs-md-tab-text-transition;
+}
+
+.tabs-md[tabsLayout=icon-top] .has-icon .tab-button-text {
+ @include margin(4px, null, 0, null);
+}
+
+.tabs-md[tabsLayout=icon-bottom] .tab-button .tab-button-text {
+ @include margin(0, null, null, null);
+}
+
+
+// Material Design Tab Button Icon
+// --------------------------------------------------
+
+.tabs-md .tab-button-icon {
+ @include margin(1px, null, null, null);
+
+ min-width: $tabs-md-tab-icon-size;
+
+ font-size: $tabs-md-tab-icon-size;
+
+ color: $tabs-md-tab-icon-color;
+
+ transition: $tabs-md-tab-icon-transition;
+
+ @include deprecated-variable(transform-origin, $tabs-md-tab-icon-transform-origin) {
+ @include transform-origin($tabs-md-tab-icon-transform-origin-x, $tabs-md-tab-icon-transform-origin-y);
+ }
+}
+
+// Tab layout: icon-top, icon-only, title-only
+.tabs-md .tab-button[aria-selected=true] .tab-button-icon {
+ color: $tabs-md-tab-icon-color-active;
+
+ @include deprecated-variable(transform, $tabs-md-tab-icon-transform-active) {
+ @include transform(translate3d($tabs-md-tab-icon-transform-x-active, $tabs-md-tab-icon-transform-y-active, $tabs-md-tab-icon-transform-z-active));
+ }
+}
+
+// Tab layout: icon-end
+.tabs-md[tabsLayout=icon-right] .tab-button[aria-selected=true] .tab-button-icon, // deprecated
+.tabs-md[tabsLayout=icon-end] .tab-button[aria-selected=true] .tab-button-icon {
+ @include deprecated-variable(transform, $tabs-md-tab-icon-end-transform-active) {
+ @include transform(translate3d($tabs-md-tab-icon-right-transform-x-active, $tabs-md-tab-icon-right-transform-y-active, $tabs-md-tab-icon-right-transform-z-active));
+ }
+}
+
+// Tab layout: icon-bottom
+.tabs-md[tabsLayout=icon-bottom] .tab-button[aria-selected=true] .tab-button-icon {
+ @include deprecated-variable(transform, $tabs-md-tab-icon-bottom-transform-active) {
+ @include transform(translate3d($tabs-md-tab-icon-bottom-transform-x-active, $tabs-md-tab-icon-bottom-transform-y-active, $tabs-md-tab-icon-bottom-transform-z-active));
+ }
+}
+
+// Tab layout: icon-start
+.tabs-md[tabsLayout=icon-left] .tab-button[aria-selected=true] .tab-button-icon, // deprecated
+.tabs-md[tabsLayout=icon-start] .tab-button[aria-selected=true] .tab-button-icon {
+ @include deprecated-variable(transform, $tabs-md-tab-icon-start-transform-active) {
+ @include transform(translate3d($tabs-md-tab-icon-left-transform-x-active, $tabs-md-tab-icon-left-transform-y-active, $tabs-md-tab-icon-left-transform-z-active));
+ }
+}
+
+
+// Material Design Tab with Icon or Title only
+// --------------------------------------------------
+
+.tabs-md[tabsLayout=icon-hide] .tab-button,
+.tabs-md[tabsLayout=title-hide] .tab-button,
+.tabs-md .tab-button.icon-only,
+.tabs-md .tab-button.has-title-only {
+ @include padding(0, 10px);
+}
+
+
+// Material Design Tab Highlight
+// --------------------------------------------------
+
+.tabs-md[tabsHighlight=true] .tab-highlight {
+ @include position(null, null, 0, 0);
+ @include transform-origin(0, 0);
+
+ position: absolute;
+
+ display: block;
+
+ width: 1px;
+ height: 2px;
+
+ background: $tabs-md-tab-color-active;
+ transform: translateZ(0);
+}
+
+.tabs-md[tabsHighlight=true] .tab-highlight.animate {
+ transition-duration: 300ms;
+}
+
+.tabs-md[tabsHighlight=true][tabsPlacement=bottom] > .tabbar > .tab-highlight {
+ top: 0;
+}
+
+
+// Material Design Tabs Color Mixin
+// --------------------------------------------------
+
+@mixin tabbar-md($color-name, $color-base, $color-contrast) {
+
+ .tabs-md-#{$color-name} .tabbar {
+ background-color: $color-base;
+ }
+
+ .tabs-md-#{$color-name} .tab-button,
+ .tabs-md-#{$color-name} .tab-button-icon {
+ color: rgba($color-contrast, $tabs-md-tab-opacity);
+ }
+
+ .tabs-md-#{$color-name} .tab-button:hover:not(.disable-hover),
+ .tabs-md-#{$color-name} .tab-button[aria-selected=true],
+ .tabs-md-#{$color-name} .tab-button[aria-selected=true] .tab-button-icon {
+ color: $color-contrast;
+ }
+
+ .tabs-md-#{$color-name}[tabsHighlight=true] .tab-highlight {
+ background: $color-contrast;
+ }
+
+}
+
+
+// Material Design Tabbar Color Generation
+// --------------------------------------------------
+
+@each $color-name, $color-base, $color-contrast in get-colors($colors-md) {
+ @include tabbar-md($color-name, $color-base, $color-contrast);
+}
diff --git a/packages/core/src/components/tabs/tabs.scss b/packages/core/src/components/tabs/tabs.scss
new file mode 100644
index 0000000000..73473a3572
--- /dev/null
+++ b/packages/core/src/components/tabs/tabs.scss
@@ -0,0 +1,153 @@
+@import "../../themes/ionic.globals";
+
+// Tabs
+// --------------------------------------------------
+
+.tabbar {
+ @include position(null, null, 0, 0);
+
+ position: absolute;
+
+ z-index: $z-index-toolbar;
+ display: flex;
+
+ width: 100%;
+
+ // default to hidden until ready
+ //opacity: 0;
+}
+
+.tabbar-hidden .tabbar {
+ display: none;
+}
+
+.tabbar.show-tabbar {
+ opacity: 1;
+}
+
+[tabsPlacement=top] > .tabbar {
+ top: 0;
+ bottom: auto;
+}
+
+.tab-button {
+ @include margin(0);
+ @include text-align(center);
+ @include border-radius(0);
+
+ position: relative;
+ z-index: 0;
+ display: flex;
+ overflow: hidden;
+
+ flex: 1;
+ flex-direction: column;
+ align-items: center;
+ align-self: center;
+ justify-content: center;
+
+ border: 0;
+
+ text-decoration: none;
+ background: none;
+ cursor: pointer;
+
+ user-select: none;
+}
+
+.tab-disabled {
+ pointer-events: none;
+}
+
+.tab-disabled ion-badge,
+.tab-disabled ion-icon,
+.tab-disabled span {
+ opacity: .4;
+}
+
+.tab-button-text {
+ @include margin(3px, null, 2px, null);
+}
+
+.tab-button-text,
+.tab-button-icon {
+ //display: none;
+ overflow: hidden;
+
+ align-self: center;
+
+ min-width: 26px;
+ max-width: 100%;
+
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.has-icon .tab-button-icon,
+.has-title .tab-button-text {
+ display: block;
+}
+
+.has-title-only .tab-button-text {
+ white-space: normal;
+}
+
+[tabsLayout=icon-bottom] .tab-button .tab-button-icon {
+ order: 10;
+}
+
+[tabsLayout=icon-left] .tab-button, // deprecated
+[tabsLayout=icon-right] .tab-button, // deprecated
+[tabsLayout=icon-start] .tab-button,
+[tabsLayout=icon-end] .tab-button {
+ flex-direction: row;
+}
+
+[tabsLayout=icon-left] .tab-button .tab-button-icon, // deprecated
+[tabsLayout=icon-start] .tab-button .tab-button-icon {
+ @include padding-horizontal(null, 8px);
+ @include text-align(end);
+}
+
+[tabsLayout=icon-right] .tab-button .tab-button-icon, // deprecated
+[tabsLayout=icon-end] .tab-button .tab-button-icon {
+ @include padding-horizontal(8px, null);
+ @include text-align(start);
+
+ order: 10;
+}
+
+.tab-hidden,
+.tab-highlight,
+[tabsLayout=icon-hide] .tab-button-icon,
+[tabsLayout=title-hide] .tab-button-text {
+ display: none;
+}
+
+// Tab Badges
+// --------------------------------------------------
+
+.tab-badge {
+ @include position(6%, 4%, null, null); // 4% fallback
+ @include position(null, calc(50% - 50px), null, null);
+ @include padding(1px, 6px);
+
+ position: absolute;
+
+ height: auto;
+
+ font-size: 12px;
+ line-height: 16px;
+}
+
+.has-icon .tab-badge {
+ @include position(null, calc(50% - 30px), null, null);
+}
+
+[tabsLayout=icon-bottom] .tab-badge,
+[tabsLayout=icon-left] .tab-badge, // deprecated
+[tabsLayout=icon-right] .tab-badge, // deprecated
+[tabsLayout=icon-start] .tab-badge,
+[tabsLayout=icon-end] .tab-badge {
+ @include position(null, calc(50% - 50px), null, null);
+}
diff --git a/packages/core/src/components/tabs/tabs.tsx b/packages/core/src/components/tabs/tabs.tsx
new file mode 100644
index 0000000000..3323f951b4
--- /dev/null
+++ b/packages/core/src/components/tabs/tabs.tsx
@@ -0,0 +1,96 @@
+import { Component, h, Listen } from '@stencil/core';
+
+@Component({
+ tag: 'ion-tabs',
+ styleUrls: {
+ ios: 'tabs.ios.scss',
+ md: 'tabs.md.scss',
+ wp: 'tabs.wp.scss'
+ },
+ host: {
+ theme: 'tabs'
+ }
+})
+export class Tabs {
+ // Current list of tabs
+ @State() tabs: [Tab] = []
+
+ /**
+ * @state {number} The selected tab
+ */
+ @State() selectedTab: Tab;
+
+ /**
+ * @state {number} The selected tab index
+ */
+ @State() selectedIndex: number = 0;
+
+ /**
+ * @prop {string} Set the tabbar layout: `icon-top`, `icon-start`, `icon-end`, `icon-bottom`, `icon-hide`, `title-hide`.
+ */
+ @Prop() tabsLayout: string = 'icon-top'
+
+ /**
+ * @prop {string} Set position of the tabbar: `top`, `bottom`.
+ */
+ @Prop() tabsPlacement: string = 'bottom';
+
+ /**
+ * @prop {boolean} If true, show the tab highlight bar under the selected tab.
+ */
+ @Prop() tabsHighlight: boolean = false;
+
+
+ /**
+ * @output {any} Emitted when the tab changes.
+ */
+ @Prop() ionChange: Function;
+
+ /**
+ * If selectedIndex was changed, grab the reference to the tab it points to.
+ */
+ @PropDidChange('selectedIndex')
+ handleSelectedIndexChanged() {
+ this.selectedTab = this.tabs[this.selectedIndex]
+ }
+
+ @Listen('ionTabDidLoad')
+ tabDidLoad(ev) {
+ const tab = ev.detail.tab;
+
+ // First tab? Select it
+ if(this.tabs.length == 0) {
+ this.handleOnTabSelected(tab, 0);
+ }
+
+ this.tabs = [ ...this.tabs, tab ];
+ }
+
+ @Listen('ionTabDidUnload')
+ tabDidUnload(ev) {
+ this.tabs = this.tabs.filter(t => t !== ev.detail.tab)
+ }
+
+ handleOnTabSelected(tab, index) {
+ // Select just this tab
+ this.tabs.forEach(t => t.isSelected = false);
+ tab.isSelected = true;
+
+ // Store the selected tab and index
+ this.selectedTab = tab;
+ this.selectedIndex = index;
+
+ // Fire a change event
+ this.ionChange && this.ionChange(tab)
+ }
+
+ render() {
+ return [
+ ,
+
+ ]
+ }
+}
diff --git a/packages/core/src/components/tabs/tabs.wp.scss b/packages/core/src/components/tabs/tabs.wp.scss
new file mode 100644
index 0000000000..5193034371
--- /dev/null
+++ b/packages/core/src/components/tabs/tabs.wp.scss
@@ -0,0 +1,178 @@
+@import "../../themes/ionic.globals.wp";
+@import "./tabs";
+
+
+// Windows Tabs
+// --------------------------------------------------
+
+// deprecated
+$tabs-wp-tab-padding: null !default;
+
+/// @prop - Padding top on the tab button
+$tabs-wp-tab-padding-top: 12px !default;
+
+/// @prop - Padding end on the tab button
+$tabs-wp-tab-padding-end: 10px !default;
+
+/// @prop - Padding bottom on the tab button
+$tabs-wp-tab-padding-bottom: 5px !default;
+
+/// @prop - Padding start on the tab button
+$tabs-wp-tab-padding-start: 10px !default;
+
+/// @prop - Minimum height of the tab button
+$tabs-wp-tab-min-height: 4.8rem !default;
+
+/// @prop - Font size of the tab button text
+$tabs-wp-tab-font-size: 1.2rem !default;
+
+/// @prop - Font weight of the tab button text
+$tabs-wp-tab-font-weight: normal !default;
+
+/// @prop - Opacity of the inactive tab button
+$tabs-wp-tab-opacity: .7 !default;
+
+/// @prop - Text color of the inactive tab button
+$tabs-wp-tab-color: rgba($tabs-wp-tab-color-inactive, $tabs-wp-tab-opacity) !default;
+
+/// @prop - Text color of the active tab button
+$tabs-wp-tab-color-active: $tabs-wp-tab-color-active !default;
+
+/// @prop - Icon color of the inactive tab button
+$tabs-wp-tab-icon-color: rgba($tabs-wp-tab-color-inactive, $tabs-wp-tab-opacity) !default;
+
+/// @prop - Icon color of the active tab button
+$tabs-wp-tab-icon-color-active: $tabs-wp-tab-color-active !default;
+
+/// @prop - Border on the inactive tab button (border-top when [tabsPlacement=bottom] and border-bottom when [tabsPlacement=top])
+$tabs-wp-tab-border: 2px solid $tabs-wp-border-color !default;
+
+/// @prop - Border color on the active tab button (border-top when [tabsPlacement=bottom] and border-bottom when [tabsPlacement=top])
+$tabs-wp-tab-border-color-active: $tabs-wp-tab-color-active !default;
+
+/// @prop - Background of the tab button when pressed
+$tabs-wp-tab-background-activated: rgba(0, 0, 0, .1) !default;
+
+/// @prop - Size of the tab button icon
+$tabs-wp-tab-icon-size: 2.4rem !default;
+
+
+.tabs-wp .tabbar {
+ background: $tabs-wp-background;
+}
+
+.tabs-wp .tab-button {
+ @include border-radius(0);
+
+ min-height: $tabs-wp-tab-min-height;
+
+ border-bottom: $tabs-wp-tab-border;
+ font-size: $tabs-wp-tab-font-size;
+ font-weight: $tabs-wp-tab-font-weight;
+ color: $tabs-wp-tab-color;
+ box-shadow: none;
+
+ @include deprecated-variable(padding, $tabs-wp-tab-padding) {
+ @include padding($tabs-wp-tab-padding-top, $tabs-wp-tab-padding-end, $tabs-wp-tab-padding-bottom, $tabs-wp-tab-padding-start);
+ }
+}
+
+.tabs-wp .tab-button[aria-selected=true] {
+ border-bottom-color: $tabs-wp-tab-border-color-active;
+ color: $tabs-wp-tab-color-active;
+}
+
+.tabs-wp .tab-button.activated {
+ background: $tabs-wp-tab-background-activated;
+}
+
+.tabs-wp[tabsPlacement=bottom] .tab-button {
+ border-top: $tabs-wp-tab-border;
+ border-bottom-width: 0;
+}
+
+.tabs-wp[tabsPlacement=bottom] .tab-button[aria-selected=true] {
+ border-top-color: $tabs-wp-tab-border-color-active;
+}
+
+
+// Windows Tab Button Text
+// --------------------------------------------------
+
+.tabs-wp .tab-button-text {
+ @include margin(5px, null);
+}
+
+
+// Windows Tab Button Icon
+// --------------------------------------------------
+
+.tabs-wp .tab-button-icon {
+ min-width: $tabs-wp-tab-icon-size;
+
+ font-size: $tabs-wp-tab-icon-size;
+
+ color: $tabs-wp-tab-icon-color;
+}
+
+.tabs-wp .tab-button[aria-selected=true] .tab-button-icon {
+ color: $tabs-wp-tab-icon-color-active;
+}
+
+.tabs-wp[tabsLayout=icon-bottom] .tab-button {
+ @include padding(8px, null, 8px, null);
+}
+
+.tabs-wp[tabsLayout=icon-right] .tab-button, // deprecated
+.tabs-wp[tabsLayout=icon-left] .tab-button, // deprecated
+.tabs-wp[tabsLayout=icon-end] .tab-button,
+.tabs-wp[tabsLayout=icon-start] .tab-button {
+ @include padding(null, null, 10px, null);
+}
+
+.tabs-wp[tabsLayout=icon-right] .tab-button ion-icon, // deprecated
+.tabs-wp[tabsLayout=icon-left] .tab-button ion-icon, // deprecated
+.tabs-wp[tabsLayout=icon-end] .tab-button ion-icon,
+.tabs-wp[tabsLayout=icon-start] .tab-button ion-icon {
+ min-width: 24px;
+}
+
+.tabs-wp[tabsLayout=icon-hide] .tab-button,
+.tabs-wp[tabsLayout=title-hide] .tab-button,
+.tabs-wp .tab-button.icon-only,
+.tabs-wp .tab-button.has-title-only {
+ @include padding(6px, 10px);
+}
+
+
+// Windows Tabbar Color Mixin
+// --------------------------------------------------
+
+@mixin tabbar-wp($color-name, $color-base, $color-contrast) {
+
+ .tabs-wp-#{$color-name} .tabbar {
+ background-color: $color-base;
+ }
+
+ .tabs-wp-#{$color-name} .tab-button,
+ .tabs-wp-#{$color-name} .tab-button-icon {
+ color: rgba($color-contrast, $tabs-wp-tab-opacity);
+ }
+
+ .tabs-wp-#{$color-name} .tab-button:hover:not(.disable-hover),
+ .tabs-wp-#{$color-name} .tab-button:hover:not(.disable-hover) .tab-button-icon,
+ .tabs-wp-#{$color-name} .tab-button[aria-selected=true],
+ .tabs-wp-#{$color-name} .tab-button[aria-selected=true] .tab-button-icon {
+ border-color: $color-contrast;
+ color: $color-contrast;
+ }
+
+}
+
+
+// Windows Tabbar Color Generation
+// --------------------------------------------------
+
+@each $color-name, $color-base, $color-contrast in get-colors($colors-wp) {
+ @include tabbar-wp($color-name, $color-base, $color-contrast);
+}