fix(tab-bar): update to match MD design and remove transforms (#16348)

Updates the tab-bar to match the MD bottom navigation spec: https://material.io/design/components/bottom-navigation.html#specs

Changes proposed in this pull request:
- moved flex properties to host, inherit in child anchor element
  - this allows the user to customize the tab-button flex properties & fixes #16231
- increased letter spacing
- removed transforms / transitions on active tab
- decreased badge size, add badge styling when empty for MD
- fix badge positioning on both iOS and MD
- updates e2e tests, adds a preview test for tab-bar
- updates documentation surrounding tabs

fixes #16231
fixes ionic-team/ionic-docs#175
fixes ionic-team/ionic-docs#163
This commit is contained in:
Brandy Carney
2018-11-21 12:07:37 -05:00
committed by GitHub
parent d336054328
commit bc3e192427
18 changed files with 904 additions and 327 deletions

View File

@ -1,25 +1,6 @@
# ion-tab-bar
Tab bar is the UI component that implements the array of button of `ion-tabs`. It's provided by default when `ion-tabs` is used, though, this "implicit" tab bar can not be customized.
In order to have a custom tab bar, it should be provided in user's markup as direct children of `ion-tabs`:
```html
<style>
ion-tab-bar {
font-size: 20px;
}
</style>
<ion-tabs>
<!-- User tabs -->
<ion-tab></ion-tab>
<ion-tab></ion-tab>
<!-- User provided ion-tab-bar that can be customized -->
<ion-tab-bar slot="bottom" color="dark" layout="icon-only">
</ion-tabs>
```
The tab bar is a UI component that contains a set of [tab buttons](../tab-button). A tab bar must be provided inside of [tabs](../tabs) to communicate with each [tab](../tab).
<!-- Auto Generated Below -->

View File

@ -7,10 +7,7 @@
* @prop --color: Color of the tab bar
*/
@include padding-horizontal(
var(--ion-safe-area-left),
var(--ion-safe-area-right)
);
@include padding-horizontal(var(--ion-safe-area-left), var(--ion-safe-area-right));
display: flex;
@ -33,13 +30,14 @@
}
:host(.ion-color) {
--background: #{current-color(base)};
background: #{current-color(base)};
}
:host(.ion-color) ::slotted(ion-tab-button) {
--color: #{current-color(contrast, .7)};
--color-selected: #{current-color(contrast)};
--background-focused: #{current-color(shade)};
--color-selected: #{current-color(contrast)};;
color: #{current-color(contrast, .7)};
}
:host([slot="top"]) {

View File

@ -0,0 +1,10 @@
import { newE2EPage } from '@stencil/core/testing';
test('tab-bar: preview', async () => {
const page = await newE2EPage({
url: '/src/components/tab-bar/test/preview?ionic:_testing=true'
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});

View File

@ -0,0 +1,202 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Tab Bar - Preview</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<script src="../../../../../dist/ionic.js"></script>
<link rel="stylesheet" href="../../../../../css/ionic.bundle.css">
<link rel="stylesheet" href="../../../../../scripts/testing/styles.css">
</head>
<body>
<!-- Default -->
<ion-tab-bar selected-tab="1">
<ion-tab-button tab="1">
<ion-label>Recents</ion-label>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-label>Favorites</ion-label>
<ion-badge>23</ion-badge>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-label>Settings</ion-label>
</ion-tab-button>
</ion-tab-bar>
<!-- Badges -->
<ion-tab-bar selected-tab="1">
<ion-tab-button tab="1" layout="icon-top">
<ion-icon name="heart"></ion-icon>
<ion-label>Favorites</ion-label>
<ion-badge color="danger"></ion-badge>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-icon name="musical-note"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-icon name="today"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="4">
<ion-icon name="calendar"></ion-icon>
<ion-badge color="danger">47</ion-badge>
</ion-tab-button>
</ion-tab-bar>
<ion-tab-bar selected-tab="1">
<ion-tab-button tab="1">
<ion-icon name="musical-note"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2" layout="icon-bottom">
<ion-icon name="heart"></ion-icon>
<ion-label>Favorites</ion-label>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-icon name="today"></ion-icon>
<ion-badge color="danger">88</ion-badge>
</ion-tab-button>
<ion-tab-button tab="4">
<ion-icon name="calendar"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<ion-tab-bar selected-tab="1">
<ion-tab-button tab="1">
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-icon name="musical-note"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-icon name="today"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<!-- Icons on top of text -->
<ion-tab-bar color="secondary" selected-tab="1">
<ion-tab-button tab="1">
<ion-label>Location</ion-label>
<ion-icon name="navigate"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-badge color="danger">44</ion-badge>
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-label>Radio</ion-label>
<ion-icon name="musical-notes"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<!-- Icons below text -->
<ion-tab-bar color="dark" selected-tab="1">
<ion-tab-button tab="1" layout="icon-bottom">
<ion-label>Recents</ion-label>
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2" layout="icon-top">
<ion-badge>16</ion-badge>
<ion-label>Favorites</ion-label>
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3" layout="icon-bottom">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<!-- Icons right of text -->
<ion-tab-bar color="danger" selected-tab="1">
<ion-tab-button tab="1" layout="icon-end">
<ion-label>Recents</ion-label>
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2" layout="icon-end">
<ion-label>Favorites</ion-label>
<ion-icon name="heart"></ion-icon>
<ion-badge color="dark">33</ion-badge>
</ion-tab-button>
<ion-tab-button tab="3" layout="icon-end">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<!-- Icons left of text -->
<ion-tab-bar color="light" selected-tab="1">
<ion-tab-button tab="1" layout="icon-start">
<ion-label>Recents</ion-label>
<ion-icon name="call"></ion-icon>
<ion-badge color="danger">12</ion-badge>
</ion-tab-button>
<ion-tab-button tab="2" layout="icon-start">
<ion-label>Favorites</ion-label>
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3" layout="icon-start">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<!-- No icons -->
<ion-tab-bar color="primary" selected-tab="1">
<ion-tab-button tab="1" layout="icon-hide">
<ion-label>Recents</ion-label>
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2" layout="icon-hide">
<ion-label>Favorites</ion-label>
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3" layout="icon-hide">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
<ion-badge color="danger">2</ion-badge>
</ion-tab-button>
</ion-tab-bar>
<style>
body {
background: #f6f6f6;
}
ion-tab-bar {
margin-bottom: 10px;
}
</style>
</body>
</html>

View File

@ -3,228 +3,250 @@
<head>
<meta charset="UTF-8">
<title>Tab - Colors</title>
<title>Tab Bar - Scenarios</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<script src="../../../../../dist/ionic.js"></script>
<link rel="stylesheet" href="../../../../../css/ionic.bundle.css">
<link rel="stylesheet" href="../../../../../scripts/testing/styles.css">
<style>
.custom-tabbar {
overflow: visible;
contain: none;
}
.main-tab {
--padding-top: 18px;
border-radius: 50%;
max-width: 80px;
height: 80px;
background: #848484;
color: white;
font-size: 16px;
}
</style>
</head>
<body>
<ion-app>
<!-- Default -->
<ion-tab-bar selected-tab="1">
<!-- Default -->
<ion-tab-bar selected-tab="1">
<ion-tab-button tab="1">
<ion-label>Recents</ion-label>
</ion-tab-button>
<ion-tab-button tab='1'>
<ion-label>Recents</ion-label>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-label>Favorites</ion-label>
<ion-badge>6</ion-badge>
</ion-tab-button>
<ion-tab-button tab='2'>
<ion-label>Favorites</ion-label>
<ion-badge>6</ion-badge>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-label>Settings</ion-label>
</ion-tab-button>
<ion-tab-button tab='3'>
<ion-label>Settings</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tab-bar>
<!-- Icons -->
<ion-tab-bar color="primary" selected-tab="1">
<ion-tab-button tab='1'>
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<!-- Icons -->
<ion-tab-bar color="primary" selected-tab="1">
<ion-tab-button tab="1">
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<ion-tab-button tab='2' selected-tab="1">
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2" selected-tab="1">
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab='3'>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<ion-tab-button tab="3">
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<!-- Icons on top of text -->
<ion-tab-bar color="secondary" selected-tab="1">
<ion-tab-button tab='1'>
<ion-label>Location</ion-label>
<ion-icon name="navigate"></ion-icon>
</ion-tab-button>
<!-- Icons on top of text -->
<ion-tab-bar color="secondary" selected-tab="1">
<ion-tab-button tab="1">
<ion-label>Location</ion-label>
<ion-icon name="navigate"></ion-icon>
</ion-tab-button>
<ion-tab-button tab='2'>
<ion-badge>6</ion-badge>
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-badge>6</ion-badge>
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab='3'>
<ion-label>Radio</ion-label>
<ion-icon name="musical-notes"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-label>Radio</ion-label>
<ion-icon name="musical-notes"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
</ion-tab-bar>
<!-- Icons below text -->
<ion-tab-bar color="dark" selected-tab="1">
<!-- Icons below text -->
<ion-tab-bar color="dark" selected-tab="1">
<ion-tab-button tab='1' layout="icon-bottom">
<ion-label>Recents</ion-label>
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="1" layout="icon-bottom">
<ion-label>Recents</ion-label>
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<ion-tab-button tab='2' layout="icon-top">
<ion-badge>6</ion-badge>
<ion-label>hi</ion-label>
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2" layout="icon-top">
<ion-badge>6</ion-badge>
<ion-label>hi</ion-label>
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab='3' layout="icon-bottom">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3" layout="icon-bottom">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
</ion-tab-bar>
<!-- Icons right of text -->
<ion-tab-bar color="danger" selected-tab="1">
<ion-tab-button tab='1' layout="icon-end">
<ion-label>Recents</ion-label>
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<!-- Icons right of text -->
<ion-tab-bar color="danger" selected-tab="1">
<ion-tab-button tab="1" layout="icon-end">
<ion-label>Recents</ion-label>
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<ion-tab-button tab='2' layout="icon-end">
<ion-label>Favorites</ion-label>
<ion-icon name="heart"></ion-icon>
<ion-badge>6</ion-badge>
</ion-tab-button>
<ion-tab-button tab="2" layout="icon-end">
<ion-label>Favorites</ion-label>
<ion-icon name="heart"></ion-icon>
<ion-badge>6</ion-badge>
</ion-tab-button>
<ion-tab-button tab='3' layout="icon-end">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3" layout="icon-end">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
</ion-tab-bar>
<!-- Icons left of text -->
<ion-tab-bar color="light" selected-tab="1">
<ion-tab-button tab='1' layout="icon-start">
<ion-label>Recents</ion-label>
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<!-- Icons left of text -->
<ion-tab-bar color="light" selected-tab="1">
<ion-tab-button tab="1" layout="icon-start">
<ion-label>Recents</ion-label>
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<ion-tab-button tab='2' layout="icon-start">
<ion-label>Favorites</ion-label>
<ion-icon name="heart"></ion-icon>
<ion-badge color="danger">6</ion-badge>
</ion-tab-button>
<ion-tab-button tab="2" layout="icon-start">
<ion-label>Favorites</ion-label>
<ion-icon name="heart"></ion-icon>
<ion-badge color="danger">6</ion-badge>
</ion-tab-button>
<ion-tab-button tab='3' layout="icon-start">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3" layout="icon-start">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
</ion-tab-bar>
<!-- No icons -->
<ion-tab-bar color="primary" selected-tab="1">
<ion-tab-button tab='1' layout="icon-hide">
<ion-label>Recents</ion-label>
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<!-- No icons -->
<ion-tab-bar color="primary" selected-tab="1">
<ion-tab-button tab="1" layout="icon-hide">
<ion-label>Recents</ion-label>
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<ion-tab-button tab='2' layout="icon-hide">
<ion-label>Favorites</ion-label>
<ion-icon name="heart"></ion-icon>
<ion-badge color="danger">6</ion-badge>
</ion-tab-button>
<ion-tab-button tab="2" layout="icon-hide">
<ion-label>Favorites</ion-label>
<ion-icon name="heart"></ion-icon>
<ion-badge color="danger">6</ion-badge>
</ion-tab-button>
<ion-tab-button tab='3' layout="icon-hide">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<ion-tab-button tab="3" layout="icon-hide">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<!-- No label -->
<ion-tab-bar color="secondary" selected-tab="1">
<ion-tab-button tab='1' layout="label-hide">
<ion-label>Recents</ion-label>
<ion-icon name="navigate"></ion-icon>
<ion-badge>6</ion-badge>
</ion-tab-button>
<!-- No label -->
<ion-tab-bar color="secondary" selected-tab="1">
<ion-tab-button tab="1" layout="label-hide">
<ion-label>Recents</ion-label>
<ion-icon name="navigate"></ion-icon>
<ion-badge>6</ion-badge>
</ion-tab-button>
<ion-tab-button tab='2' layout="label-hide">
<ion-label>Favorites</ion-label>
<ion-icon name="heart"></ion-icon>
<ion-badge color="danger">6</ion-badge>
</ion-tab-button>
<ion-tab-button tab="2" layout="label-hide">
<ion-label>Favorites</ion-label>
<ion-icon name="heart"></ion-icon>
<ion-badge color="danger">6</ion-badge>
</ion-tab-button>
<ion-tab-button tab='3' layout="label-hide">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<ion-tab-button tab="3" layout="label-hide">
<ion-label>Settings</ion-label>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<!-- No overflow text -->
<ion-tab-bar color="danger" selected-tab="1">
<ion-tab-button tab='1'>
<ion-label>Indiana Jones and the Raiders of the Lost Ark</ion-label>
</ion-tab-button>
<!-- No overflow text -->
<ion-tab-bar color="danger" selected-tab="1">
<ion-tab-button tab="1">
<ion-label>Indiana Jones and the Raiders of the Lost Ark</ion-label>
</ion-tab-button>
<ion-tab-button tab='2'>
<ion-label>Indiana Jones and the Temple of Doom</ion-label>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-label>Indiana Jones and the Temple of Doom</ion-label>
</ion-tab-button>
<ion-tab-button tab='3'>
<ion-label>Indiana Jones and the Last Crusade</ion-label>
</ion-tab-button>
</ion-tab-bar>
<ion-tab-button tab="3">
<ion-label>Indiana Jones and the Last Crusade</ion-label>
</ion-tab-button>
</ion-tab-bar>
<!-- Custom TabBar -->
<ion-tab-bar selected-tab="1" class="custom-tabbar">
<ion-tab-button tab='1'>
<ion-label>Location</ion-label>
<ion-icon name="navigate"></ion-icon>
</ion-tab-button>
<!-- Custom Tab Bar -->
<ion-tab-bar selected-tab="1" class="custom-tabbar">
<ion-tab-button tab="1">
<ion-label>Location</ion-label>
<ion-icon name="navigate"></ion-icon>
</ion-tab-button>
<ion-tab-button tab='2' class="main-tab">
<ion-icon src="/src/components/tab-bar/test/scenarios/camera.svg"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2" class="main-tab">
<ion-icon src="/src/components/tab-bar/test/scenarios/camera.svg"></ion-icon>
</ion-tab-button>
<ion-tab-button tab='3'>
<ion-label>Radio</ion-label>
<ion-icon name="musical-notes"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-label>Radio</ion-label>
<ion-icon name="musical-notes"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
</ion-tab-bar>
</ion-app>
<!-- Custom Tab Bar Colors -->
<ion-tab-bar selected-tab="1" class="custom-tabbar-color">
<ion-tab-button tab="1">
<ion-label>Location</ion-label>
<ion-icon name="navigate"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-label>Radio</ion-label>
<ion-icon name="musical-notes"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<style>
.custom-tabbar {
margin-top: 30px;
margin-bottom: 30px;
overflow: visible;
contain: none;
}
.main-tab {
border-radius: 50%;
max-width: 80px;
height: 80px;
background: #3880ff;
color: white;
font-size: 16px;
}
.custom-tabbar-color {
--background: #7044ff;
}
.custom-tabbar-color ion-tab-button {
--color: rgba(255, 255, 255, .7);
--color-selected: white;
}
</style>
</body>
</html>

View File

@ -0,0 +1,10 @@
import { newE2EPage } from '@stencil/core/testing';
test('tab-bar: spec', async () => {
const page = await newE2EPage({
url: '/src/components/tab-bar/test/spec?ionic:_testing=true'
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});

View File

@ -0,0 +1,169 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Tab Bar - Spec</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<script src="../../../../../dist/ionic.js"></script>
<link rel="stylesheet" href="../../../../../css/ionic.bundle.css">
<link rel="stylesheet" href="../../../../../scripts/testing/styles.css">
</head>
<body>
<!-- Default -->
<ion-tab-bar selected-tab="1">
<ion-tab-button tab="1">
<ion-label>Recents</ion-label>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-label>Favorites</ion-label>
<ion-badge>6</ion-badge>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-label>Settings</ion-label>
</ion-tab-button>
</ion-tab-bar>
<!-- Badges -->
<ion-tab-bar selected-tab="1">
<ion-tab-button tab="1" layout="icon-top">
<ion-icon name="heart"></ion-icon>
<ion-label>Item One Max</ion-label>
<ion-badge color="danger"></ion-badge>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-icon name="musical-note"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-icon name="today"></ion-icon>
<ion-badge color="danger">88</ion-badge>
</ion-tab-button>
<ion-tab-button tab="4">
<ion-icon name="calendar"></ion-icon>
<ion-badge color="danger">888+</ion-badge>
</ion-tab-button>
</ion-tab-bar>
<ion-tab-bar selected-tab="1">
<ion-tab-button tab="1">
<ion-icon name="musical-note"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2" layout="icon-bottom">
<ion-icon name="heart"></ion-icon>
<ion-label>Item One Max</ion-label>
<ion-badge color="danger"></ion-badge>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-icon name="today"></ion-icon>
<ion-badge color="danger">88</ion-badge>
</ion-tab-button>
<ion-tab-button tab="4">
<ion-icon name="calendar"></ion-icon>
<ion-badge color="danger">888+</ion-badge>
</ion-tab-button>
</ion-tab-bar>
<ion-tab-bar selected-tab="1">
<ion-tab-button tab="1">
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-icon name="musical-note"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-icon name="today"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<ion-tab-bar selected-tab="1">
<ion-tab-button tab="1">
<ion-icon name="musical-note"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2" layout="icon-bottom">
<ion-icon name="heart"></ion-icon>
<ion-label>Item One Max</ion-label>
<ion-badge color="danger">88</ion-badge>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-icon name="today"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="4">
<ion-icon name="calendar"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<!-- Custom Height -->
<ion-tab-bar color="danger" selected-tab="3" class="custom-height">
<ion-tab-button tab="1">
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-icon name="musical-note"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-icon name="today"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="4">
<ion-icon name="calendar"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<!-- Custom Height / Alignment -->
<ion-tab-bar color="tertiary" selected-tab="3" class="custom-height custom-top">
<ion-tab-button tab="1">
<ion-icon name="heart"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="2">
<ion-icon name="musical-note"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="3">
<ion-icon name="today"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="4">
<ion-icon name="calendar"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
<style>
body {
background: #222;
}
ion-tab-bar {
margin-bottom: 5px;
}
.custom-height {
height: 80px;
}
.custom-top ion-tab-button {
justify-content: start;
}
</style>
</body>
</html>

View File

@ -0,0 +1,21 @@
```html
<ion-tabs>
<!-- Tab views -->
<ion-tab tab="account"></ion-tab>
<ion-tab tab="contact"></ion-tab>
<ion-tab tab="settings"></ion-tab>
<!-- Tab bar -->
<ion-tab-bar slot="bottom">
<ion-tab-button tab="account">
<ion-icon name="person"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="contact">
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="settings">
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
```

View File

@ -0,0 +1,21 @@
```html
<ion-tabs>
<!-- Tab views -->
<ion-tab tab="account"></ion-tab>
<ion-tab tab="contact"></ion-tab>
<ion-tab tab="settings"></ion-tab>
<!-- Tab bar -->
<ion-tab-bar slot="bottom">
<ion-tab-button tab="account">
<ion-icon name="person"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="contact">
<ion-icon name="call"></ion-icon>
</ion-tab-button>
<ion-tab-button tab="settings">
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
```

View File

@ -1,8 +1,8 @@
# ion-tab-bar
# ion-tab-button
Tab bar is a UI component that hold the array of [tab buttons](../tab-button). The Tab bar can provide a global setting for different tab/icon layouts as well as different positions for the bar itself.
A tab button is a UI component that is placed inside of a [tab bar](../tab-bar). The tab button can specify the layout of the icon and label and connect to a [tab view](../tab).
See the [Tabs API Docs](../Tabs/) for more details on configuring Tabs.
See the [tabs documentation](../tabs) for more details on configuring tabs.
<!-- Auto Generated Below -->

View File

@ -25,12 +25,25 @@
line-height: 1.1;
}
::slotted(ion-label) {
@include margin(0, null, 1px, null);
min-height: $tab-button-ios-font-size + 1;
// iOS Tab Button: Badge
// --------------------------------------------------
::slotted(ion-badge) {
@include padding(1px, 6px);
@include position(4px, null, null, calc(50% + 6px));
height: auto;
font-size: 12px;
line-height: 16px;
}
// iOS Tab Button: Icon
// --------------------------------------------------
::slotted(ion-icon) {
@include margin(4px, null, null, null);
@ -42,7 +55,17 @@
}
// iOS TabButton Layout
// iOS Tab Button: Label
// --------------------------------------------------
::slotted(ion-label) {
@include margin(0, null, 1px, null);
min-height: $tab-button-ios-font-size + 1;
}
// iOS Tab Button Layout
// --------------------------------------------------
:host(.tab-layout-icon-end) ::slotted(ion-label),
@ -67,6 +90,54 @@
font-size: 24px;
}
// iOS Tab Button: Icon Bottom Layout
// --------------------------------------------------
// Badge
:host(.tab-layout-icon-bottom) ::slotted(ion-badge) {
@include position(null, null, null, calc(50% + 12px));
}
// Icon
:host(.tab-layout-icon-bottom) ::slotted(ion-icon) {
@include margin(0, null, 1px, null);
}
// Label
:host(.tab-layout-icon-bottom) ::slotted(ion-label) {
@include margin(4px, null, null, null);
}
// iOS Tab Button: Icon Start / End Layout
// --------------------------------------------------
:host(.tab-layout-icon-start) ::slotted(ion-badge),
:host(.tab-layout-icon-end) ::slotted(ion-badge) {
@include position(10px, null, null, calc(50% + 35px));
}
// iOS Tab Button: Icon Hide / Label Only Layout
// --------------------------------------------------------------
// Badge
:host(.tab-layout-icon-hide) ::slotted(ion-badge),
:host(.tab-has-label-only) ::slotted(ion-badge) {
@include position(10px, null, null, calc(50% + 30px));
}
// iOS Tab Button: Icon Only / Label Hide Layout
// --------------------------------------------------------------
// Badge
:host(.tab-layout-label-hide) ::slotted(ion-badge),
:host(.tab-has-icon-only) ::slotted(ion-badge) {
@include position(10px, null, null, null);
}
:host(.tab-layout-label-hide) ::slotted(ion-icon) {
@include margin(0);
}

View File

@ -2,7 +2,7 @@
@import "./tab-button.md.vars";
// Material Design Tab Button
// --------------------------------------------------
// --------------------------------------------------------------
:host {
--padding-top: #{$tab-button-md-padding-top};
@ -18,71 +18,137 @@
font-size: $tab-button-md-font-size;
font-weight: $tab-button-md-font-weight;
letter-spacing: $tab-button-md-letter-spacing;
}
// Material Design Tab Button Text
// --------------------------------------------------
// Material Design Tab Button: Label
// --------------------------------------------------------------
::slotted(ion-label) {
@include margin($tab-button-md-text-margin-top, $tab-button-md-text-margin-end, $tab-button-md-text-margin-bottom, $tab-button-md-text-margin-start);
@include transform-origin(center, bottom);
transition: $tab-button-md-text-transition;
text-transform: $tab-button-md-text-capitalization;
}
::slotted(.tab-selected) ::slotted(ion-label) {
transform: #{$tab-button-md-text-transform-active};
transition: $tab-button-md-text-transition;
}
// Material Design Tab Button Icon
// --------------------------------------------------
// Material Design Tab Button: Icon
// --------------------------------------------------------------
::slotted(ion-icon) {
@include margin($tab-button-md-icon-margin-top, $tab-button-md-icon-margin-end, $tab-button-md-icon-margin-bottom, $tab-button-md-icon-margin-start);
@include transform-origin(center, center);
width: $tab-button-md-icon-size;
height: $tab-button-md-icon-size;
transition: $tab-button-md-icon-transition;
font-size: $tab-button-md-icon-size;
}
// Material Design Tab Button: Badge
// --------------------------------------------------------------
::slotted(ion-badge) {
@include border-radius($tab-button-md-badge-border-radius);
@include padding($tab-button-md-badge-padding-top, $tab-button-md-badge-padding-end, $tab-button-md-badge-padding-bottom, $tab-button-md-badge-padding-start);
@include position(8px, null, null, calc(50% + 6px));
min-width: $tab-button-md-badge-min-width;
font-size: $tab-button-md-badge-font-size;
font-weight: normal;
}
::slotted(ion-badge:empty) {
display: block;
min-width: $tab-button-md-badge-size-empty;
height: $tab-button-md-badge-size-empty;
}
// Material Design TabButton Layout
// --------------------------------------------------
// Material Design Tab Button: Icon Top Layout
// --------------------------------------------------------------
// Icon
:host(.tab-layout-icon-top) ::slotted(ion-icon) {
@include margin(6px, null, 2px, null);
}
// Label
:host(.tab-layout-icon-top) ::slotted(ion-label) {
margin-bottom: -2px;
@include margin(0, null, 6px, null);
}
// Material Design Tab Button: Icon Bottom Layout
// --------------------------------------------------------------
// Badge
:host(.tab-layout-icon-bottom) ::slotted(ion-badge) {
@include position(8px, null, null, 70%);
}
// Icon
:host(.tab-layout-icon-bottom) ::slotted(ion-icon) {
@include margin(0, null, 6px, null);
}
// Label
:host(.tab-layout-icon-bottom) ::slotted(ion-label) {
margin-top: -2px;
// --label-transform: transform-origin(center, top);
@include margin(6px, null, 0, null);
}
:host(.tab-selected) ::slotted(ion-label) {
transform: #{$tab-button-md-text-transform-active};
// Material Design Tab Button: Icon Start / Icon End Layout
// --------------------------------------------------------------
// Badge
:host(.tab-layout-icon-start) ::slotted(ion-badge),
:host(.tab-layout-icon-end) ::slotted(ion-badge) {
@include position(16px, null, null, 80%);
}
:host(.tab-selected) ::slotted(ion-icon) {
transform: #{$tab-button-md-icon-transform-active};
// Icon
:host(.tab-layout-icon-start) ::slotted(ion-icon) {
@include margin(null, 6px, null, null);
}
:host(.tab-layout-icon-end.tab-selected) ::slotted(ion-icon) {
transform: #{$tab-button-md-icon-right-transform-active};
// Icon
:host(.tab-layout-icon-end) ::slotted(ion-icon) {
@include margin(null, null, null, 6px);
}
:host(.tab-layout-icon-bottom.tab-selected) ::slotted(ion-icon) {
transform: #{$tab-button-md-icon-bottom-transform-active};
// Material Design Tab Button: Icon Hide / Label Only Layout
// --------------------------------------------------------------
// Badge
:host(.tab-layout-icon-hide) ::slotted(ion-badge),
:host(.tab-has-label-only) ::slotted(ion-badge) {
@include position(16px, null, null, 70%);
}
:host(.tab-layout-icon-start.tab-selected) ::slotted(ion-icon) {
transform: #{$tab-button-md-icon-left-transform-active};
// Label
:host(.tab-layout-icon-hide) ::slotted(ion-label),
:host(.tab-has-label-only) ::slotted(ion-label) {
@include margin(0, null, 0, null);
}
// Material Design Tab Button: Icon Only / Label Hide Layout
// --------------------------------------------------------------
// Badge
:host(.tab-layout-label-hide) ::slotted(ion-badge),
:host(.tab-has-icon-only) ::slotted(ion-badge) {
@include position(16px, null, null, null);
}
// Icon
:host(.tab-layout-label-hide) ::slotted(ion-icon),
:host(.tab-has-icon-only) ::slotted(ion-icon) {
@include margin(0, null, 0, null);
font-size: 24px;
}

View File

@ -4,20 +4,23 @@
// --------------------------------------------------
/// @prop - Padding top on the tab button
$tab-button-md-padding-top: 8px !default;
$tab-button-md-padding-top: 0 !default;
/// @prop - Padding end on the tab button
$tab-button-md-padding-end: 12px !default;
/// @prop - Padding bottom on the tab button
$tab-button-md-padding-bottom: 10px !default;
$tab-button-md-padding-bottom: 0 !default;
/// @prop - Padding start on the tab button
$tab-button-md-padding-start: 12px, !default;
$tab-button-md-padding-start: 12px !default;
/// @prop - Font size of the inactive tab button text
$tab-button-md-font-size: 12px !default;
/// @prop - Letter spacing of the tab button
$tab-button-md-letter-spacing: .03em !default;
/// @prop - Font weight of the tab button text
$tab-button-md-font-weight: normal !default;
@ -33,14 +36,26 @@ $tab-button-md-icon-color: $tabbar-md-color !default;
/// @prop - Icon color of the active tab button
$tab-button-md-icon-color-active: $tabbar-md-color-activated !default;
/// @prop - Margin top on the tab button icon
$tab-button-md-icon-margin-top: 16px !default;
/// @prop - Margin end on the tab button icon
$tab-button-md-icon-margin-end: 0 !default;
/// @prop - Margin bottom on the tab button icon
$tab-button-md-icon-margin-bottom: $tab-button-md-icon-margin-top !default;
/// @prop - Margin start on the tab button icon
$tab-button-md-icon-margin-start: $tab-button-md-icon-margin-end !default;
/// @prop - Font size of the active tab button text
$tab-button-md-font-size-active: 14px !default;
/// @prop - Margin top on the tab button text
$tab-button-md-text-margin-top: 0 !default;
$tab-button-md-text-margin-top: 2px !default;
/// @prop - Margin end on the tab button text
$tab-button-md-text-margin-end: $tab-button-md-text-margin-top !default;
$tab-button-md-text-margin-end: 0 !default;
/// @prop - Margin bottom on the tab button text
$tab-button-md-text-margin-bottom: $tab-button-md-text-margin-top !default;
@ -51,29 +66,32 @@ $tab-button-md-text-margin-start: $tab-button-md-text-margin-end !
/// @prop - Capitalization of the tab button text
$tab-button-md-text-capitalization: none !default;
/// @prop - Transform for the active tab button text
$tab-button-md-text-transform-active: scale3d($tab-button-md-font-size-active / $tab-button-md-font-size, $tab-button-md-font-size-active / $tab-button-md-font-size, 1) !default;
/// @prop - Text transition for the tab button text
$tab-button-md-text-transition: transform .1s ease-in-out !default;
/// @prop - Transform for the active tab button icon when the layout is icon-top, icon-only, or label-only
$tab-button-md-icon-transform-active: translate3d(0, -2px, 0) !default;
/// @prop - Transform for the active tab button icon when the layout is icon-right
$tab-button-md-icon-right-transform-active: translate3d(6px, 0, 0) !default;
/// @prop - Transform for the active tab button icon when the layout is icon-bottom
$tab-button-md-icon-bottom-transform-active: translate3d(0, 2px, 0) !default;
/// @prop - Transform for the active tab button icon when the layout is icon-left
$tab-button-md-icon-left-transform-active: translate3d(-6px, 0, 0) !default;
/// @prop - Text transition for the tab button icon
$tab-button-md-icon-transition: transform .1s ease-in-out !default;
/// @prop - Size of the tab button icon
$tab-button-md-icon-size: 22px !default;
/// @prop - Opacity of the inactive tab button
$tab-button-md-opacity: .7 !default;
/// @prop - Border radius on the tab button badge
$tab-button-md-badge-border-radius: 8px !default;
/// @prop - Padding top on the tab button badge
$tab-button-md-badge-padding-top: 3px !default;
/// @prop - Padding end on the tab button badge
$tab-button-md-badge-padding-end: 2px !default;
/// @prop - Padding bottom on the tab button badge
$tab-button-md-badge-padding-bottom: 2px !default;
/// @prop - Padding start on the tab button badge
$tab-button-md-badge-padding-start: 2px !default;
/// @prop - Minimum width of the tab button badge
$tab-button-md-badge-min-width: 12px !default;
/// @prop - Font size of the tab button badge
$tab-button-md-badge-font-size: 8px !default;
/// @prop - Size of the empty tab button badge
$tab-button-md-badge-size-empty: 8px !default;

View File

@ -12,32 +12,33 @@
* @prop --padding-start: Start padding of the tab button
* @prop --ripple-color: Color of the button ripple effect
*/
--badge-end: 4%;
--ripple-color: var(--color-selected);
flex: 1;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
color: var(--color);
}
// Tab Button: Native
// --------------------------------------------------
a {
@include text-inherit();
@include margin(0);
@include padding(
var(--padding-top),
var(--padding-end),
var(--padding-bottom),
var(--padding-start),
);
@include padding(var(--padding-top), var(--padding-end), var(--padding-bottom), var(--padding-start));
@include text-inherit();
display: flex;
position: relative;
flex-direction: column;
align-items: center;
justify-content: flex-start;
flex-direction: inherit;
align-items: inherit;
justify-content: inherit;
width: 100%;
height: 100%;
@ -66,6 +67,10 @@ a:focus-visible {
}
}
// Tab Button: States
// --------------------------------------------------
:host(.tab-selected) {
color: var(--color-selected);
}
@ -81,15 +86,9 @@ a:focus-visible {
opacity: .4;
}
::slotted(ion-label) {
order: 0;
}
::slotted(ion-icon) {
order: -1;
height: 1em;
}
// Tab Button: Label / Icon
// --------------------------------------------------
::slotted(ion-label),
::slotted(ion-icon) {
@ -97,7 +96,6 @@ a:focus-visible {
align-self: center;
min-width: 26px;
max-width: 100%;
text-overflow: ellipsis;
@ -108,55 +106,49 @@ a:focus-visible {
box-sizing: border-box;
}
::slotted(ion-label) {
order: 0;
}
::slotted(ion-icon) {
order: -1;
height: 1em;
}
:host(.tab-has-label-only) ::slotted(ion-label) {
white-space: normal;
}
// Tab Badges
// Tab Button: Badge
// --------------------------------------------------
::slotted(ion-badge) {
@include position(null, var(--badge-end), null, null);
@include padding(1px, 6px);
box-sizing: border-box;
position: absolute;
height: auto;
font-size: 12px;
line-height: 16px;
z-index: 1;
}
// Tab Button Layout
// Tab Button: Layout
// --------------------------------------------------
:host(.tab-layout-icon-start) a {
:host(.tab-layout-icon-start) {
flex-direction: row;
}
:host(.tab-layout-icon-end) a {
:host(.tab-layout-icon-end) {
flex-direction: row-reverse;
}
:host(.tab-layout-icon-bottom) a {
:host(.tab-layout-icon-bottom) {
flex-direction: column-reverse;
}
:host(.tab-layout-icon-start) a,
:host(.tab-layout-icon-end) a,
:host(.tab-layout-icon-hide) a,
:host(.tab-layout-label-hide) a,
:host(.tab-has-icon-only) a,
:host(.tab-has-label-only) a {
justify-content: center;
}
:host(.tab-layout-icon-hide) ::slotted(ion-icon) {
display: none;
}
@ -165,21 +157,6 @@ a:focus-visible {
display: none;
}
:host(.tab-layout-icon-top),
:host(.tab-layout-icon-bottom),
:host(.tab-layout-icon-only),
:host(.tab-layout-label-hide),
:host(.tab-has-icon-only) {
--badge-end: #{calc(50% - 30px)};
}
:host(.tab-layout-icon-hide),
:host(.tab-layout-icon-start),
:host(.tab-layout-icon-end),
:host(.tab-has-label-only) {
--badge-end: #{calc(50% - 50px)};
}
ion-ripple-effect {
color: var(--ripple-color);
}

View File

@ -1,10 +1,8 @@
# ion-tab
The Tab component is a child component of the [Tabs](../Tabs/) component.
Each Tab is meant to be a top level navigation stack for an app.
Meaning that an app can have many tabs, all wit their own independent navigation.
The tab component is a child component of [tabs](../tabs). Each tab can contain a top level navigation stack for an app or a single view. An app can have many tabs, all with their own independent navigation.
See the [Tabs API Docs](../Tabs/) for more details on configuring Tabs.
See the [tabs documentation](../tabs/) for more details on configuring tabs.
<!-- Auto Generated Below -->

View File

@ -22,7 +22,7 @@ In order to do so, an `ion-tab-bar` should be provided as a direct child of `ion
<ion-tab-button tab="settings">
<ion-label>Settings</ion-label>
<ion-icon name="gear"></ion-icon>
<ion-icon name="settings"></ion-icon>
</ion-tab-button>
</ion-tab-bar>

View File

@ -39,5 +39,6 @@
<ion-label>About</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
```

View File

@ -1,13 +1,21 @@
```html
<ion-tabs>
<ion-tab tab="tab-schedule">
<ion-nav></ion-nav>
</ion-tab>
<ion-tab tab="tab-speaker">
<ion-nav></ion-nav>
</ion-tab>
<ion-tab tab="tab-map" component="page-map"></ion-tab>
<ion-tab tab="tab-about" component="page-about"></ion-tab>
<ion-tab tab="tab-map" component="page-map">
<ion-nav></ion-nav>
</ion-tab>
<ion-tab tab="tab-about" component="page-about">
<ion-nav></ion-nav>
</ion-tab>
<ion-tab-bar slot="bottom">
<ion-tab-button tab="tab-schedule">
@ -15,18 +23,22 @@
<ion-label>Schedule</ion-label>
<ion-badge>6</ion-badge>
</ion-tab-button>
<ion-tab-button tab="tab-speaker">
<ion-icon name="contacts"></ion-icon>
<ion-label>Speakers</ion-label>
</ion-tab-button>
<ion-tab-button tab="tab-map">
<ion-icon name="map"></ion-icon>
<ion-label>Map</ion-label>
</ion-tab-button>
<ion-tab-button tab="tab-about">
<ion-icon name="information-circle"></ion-icon>
<ion-label>About</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
```