feat(tabs): add ionic theme for tab bar and tab button (#29359)
Issue number: internal --------- ## What is the current behavior? Tab bar & tab button use the `md` styles for the `ionic` theme ## What is the new behavior? Adds the following styles for the `ionic` theme: - Rectangular, fixed tab bar - Box shadow with elevation z2 - Selected, activated, and focused states ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information - A ticket (FW-6186) has been created to track updating the hardcoded hex values to design tokens - A ticket (FW-6189) has been created to add badge styles
18
core/src/components/tab-bar/tab-bar.ionic.scss
Normal file
@@ -0,0 +1,18 @@
|
||||
@use "../../foundations/ionic.vars.scss" as tokens;
|
||||
@import "./tab-bar";
|
||||
@import "./tab-bar.ionic.vars";
|
||||
|
||||
:host {
|
||||
--background: #{$tabbar-ionic-background};
|
||||
--background-activated: #{$tabbar-ionic-background-activated};
|
||||
--background-focused: #{$tabbar-ionic-background-focused};
|
||||
--color: #{$tabbar-ionic-color};
|
||||
--color-selected: #{$tabbar-ionic-color-selected};
|
||||
|
||||
@include padding(4px, 16px);
|
||||
|
||||
height: 52px;
|
||||
gap: 12px;
|
||||
|
||||
box-shadow: tokens.$ionic-elevation-2;
|
||||
}
|
||||
20
core/src/components/tab-bar/tab-bar.ionic.vars.scss
Normal file
@@ -0,0 +1,20 @@
|
||||
@use "../../foundations/ionic.vars.scss" as tokens;
|
||||
|
||||
// Ionic Tab Bar
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Background color of the tab bar
|
||||
$tabbar-ionic-background: var(--ion-tab-bar-background, tokens.$ionic-color-neutral-0) !default;
|
||||
|
||||
/// @prop - Background color of the tab bar button when activated
|
||||
// TODO(FW-6186): use design token here when it is added
|
||||
$tabbar-ionic-background-activated: var(--ion-tab-bar-background-activated, #ececec) !default;
|
||||
|
||||
/// @prop - Background color of the tab bar button when focused
|
||||
$tabbar-ionic-background-focused: var(--ion-tab-bar-background-focused, transparent) !default;
|
||||
|
||||
/// @prop - Color of the tab bar button
|
||||
$tabbar-ionic-color: var(--ion-tab-bar-color, tokens.$ionic-color-neutral-500) !default;
|
||||
|
||||
/// @prop - Color of the selected tab bar button
|
||||
$tabbar-ionic-color-selected: var(--ion-tab-bar-color-selected, tokens.$ionic-color-primary-400) !default;
|
||||
@@ -18,7 +18,7 @@ import type { TabBarChangedEventDetail } from './tab-bar-interface';
|
||||
styleUrls: {
|
||||
ios: 'tab-bar.ios.scss',
|
||||
md: 'tab-bar.md.scss',
|
||||
ionic: 'tab-bar.md.scss',
|
||||
ionic: 'tab-bar.ionic.scss',
|
||||
},
|
||||
shadow: true,
|
||||
})
|
||||
|
||||
@@ -17,6 +17,67 @@
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-content>
|
||||
<ion-tab-bar selected-tab="1">
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3" class="ion-focused">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<ion-tab-bar selected-tab="2">
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3" class="ion-focused">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="4" class="ion-activated">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<ion-tab-bar>
|
||||
<ion-tab-button tab="1" class="ion-activated">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2" class="ion-activated">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3" class="ion-activated">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="4" class="ion-activated">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<!-- Default -->
|
||||
<ion-tab-bar selected-tab="1">
|
||||
<ion-tab-button tab="1">
|
||||
|
||||
@@ -1,6 +1,46 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
/**
|
||||
* This behavior needs to be tested in all modes and directions
|
||||
*/
|
||||
configs({ modes: ['ionic-md', 'md', 'ios'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('tab-bar: basic'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-tab-bar selected-tab="2">
|
||||
<ion-tab-button tab="1">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="2">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="3" class="ion-focused">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button tab="4" class="ion-activated">
|
||||
<ion-icon name="home-outline"></ion-icon>
|
||||
<ion-label>Label</ion-label>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const tabBar = page.locator('ion-tab-bar');
|
||||
|
||||
await expect(tabBar).toHaveScreenshot(screenshot(`tab-bar-default`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* This behavior needs to be tested in both modes and directions to
|
||||
* make sure the safe area padding is applied only to that side
|
||||
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
40
core/src/components/tab-button/tab-button.ionic.scss
Normal file
@@ -0,0 +1,40 @@
|
||||
@use "../../foundations/ionic.vars.scss" as tokens;
|
||||
@import "./tab-button";
|
||||
|
||||
// Ionic Tab Button
|
||||
// --------------------------------------------------------------
|
||||
|
||||
:host {
|
||||
// TODO(FW-6186): use design token here when it is added
|
||||
--focus-ring-color: #9ec4fd;
|
||||
--focus-ring-width: 2px;
|
||||
|
||||
@include border-radius(tokens.$ionic-border-radius-rounded-medium);
|
||||
}
|
||||
|
||||
// Tab Button Focused
|
||||
// -------------------------------------------------------------------------------
|
||||
|
||||
.button-native {
|
||||
overflow: visible;
|
||||
|
||||
&::after {
|
||||
@include border-radius(inherit);
|
||||
}
|
||||
}
|
||||
|
||||
// Only show the focus ring when the tab button is focused
|
||||
:host(.ion-focused) .button-native {
|
||||
&::after {
|
||||
outline: var(--focus-ring-width) solid var(--focus-ring-color);
|
||||
}
|
||||
}
|
||||
|
||||
// Tab Button Activated
|
||||
// -------------------------------------------------------------------------------
|
||||
|
||||
:host(.ion-activated) .button-native::after {
|
||||
background: var(--background-activated);
|
||||
|
||||
opacity: var(--background-activated-opacity);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import type {
|
||||
styleUrls: {
|
||||
ios: 'tab-button.ios.scss',
|
||||
md: 'tab-button.md.scss',
|
||||
ionic: 'tab-button.md.scss',
|
||||
ionic: 'tab-button.ionic.scss',
|
||||
},
|
||||
shadow: true,
|
||||
})
|
||||
|
||||