feat(title): support dynamic type (#27990)
Co-authored-by: ionitron <hi@ionicframework.com> Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
@@ -51,8 +51,6 @@
|
||||
}
|
||||
|
||||
.header-collapse-condense ion-toolbar:first-of-type {
|
||||
padding-top: 1px;
|
||||
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
76
core/src/components/title/test/a11y/title.e2e.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('title: font scaling'), () => {
|
||||
test('should scale default title text on larger font sizes', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<style>
|
||||
html {
|
||||
font-size: 36px;
|
||||
}
|
||||
</style>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Title</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const title = page.locator('ion-title');
|
||||
|
||||
await expect(title).toHaveScreenshot(screenshot(`title-default-scale`));
|
||||
});
|
||||
|
||||
test('should scale small title text on larger font sizes', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<style>
|
||||
html {
|
||||
font-size: 36px;
|
||||
}
|
||||
</style>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title size="small">Title</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const title = page.locator('ion-title');
|
||||
|
||||
await expect(title).toHaveScreenshot(screenshot(`title-small-scale`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('title: font scaling for large attribute'), () => {
|
||||
test('should scale text on larger font sizes', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<style>
|
||||
html {
|
||||
font-size: 36px;
|
||||
}
|
||||
</style>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title size="large">Title</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const title = page.locator('ion-title');
|
||||
|
||||
await expect(title).toHaveScreenshot(screenshot(`title-large-scale`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -14,7 +14,7 @@
|
||||
|
||||
transform: translateZ(0);
|
||||
|
||||
font-size: 17px;
|
||||
font-size: dynamic-font-max(17px, 1.2);
|
||||
font-weight: 600;
|
||||
|
||||
text-align: center;
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
position: relative;
|
||||
|
||||
font-size: 13px;
|
||||
font-size: dynamic-font-max(13px, 1.8);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@@ -40,16 +40,17 @@
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.title-large) {
|
||||
@include padding(0, 16px);
|
||||
@include padding(2px, 12px, 4px, 12px);
|
||||
@include transform-origin(left, center);
|
||||
|
||||
bottom: 0;
|
||||
position: static;
|
||||
|
||||
align-items: flex-end;
|
||||
|
||||
min-width: 100%;
|
||||
|
||||
font-size: 34px;
|
||||
font-size: dynamic-font-max(34px, 1.8);
|
||||
|
||||
font-weight: 700;
|
||||
|
||||
text-align: start;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:host {
|
||||
@include padding(0, 20px);
|
||||
|
||||
font-size: 20px;
|
||||
font-size: dynamic-font(20px);
|
||||
font-weight: 500;
|
||||
|
||||
letter-spacing: .0125em;
|
||||
@@ -20,7 +20,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
font-size: 15px;
|
||||
font-size: dynamic-font(15px);
|
||||
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@@ -79,16 +79,6 @@
|
||||
// Toolbar: Large Title
|
||||
// --------------------------------------------------
|
||||
|
||||
/**
|
||||
* Ensure there is enough spacing between the large
|
||||
* title and any potential content in subsequent
|
||||
* toolbars. We do not add padding to the title
|
||||
* itself because it is absolutely positioned.
|
||||
*/
|
||||
:host(.toolbar-title-large) {
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
|
||||
:host(.toolbar-title-large) .toolbar-container {
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
|
||||
@@ -225,10 +225,10 @@ const animateLargeTitle = (
|
||||
const BACKWARDS_KEYFRAMES = [
|
||||
{ offset: 0, opacity: 0, transform: `translate3d(${START_TRANSLATE}, ${backButtonBox.top - 4}px, 0) scale(0.49)` },
|
||||
{ offset: 0.1, opacity: 0 },
|
||||
{ offset: 1, opacity: 1, transform: `translate3d(0, ${largeTitleBox.top - 2}px, 0) scale(1)` },
|
||||
{ offset: 1, opacity: 1, transform: `translate3d(0, ${largeTitleBox.top + 2}px, 0) scale(1)` },
|
||||
];
|
||||
const FORWARDS_KEYFRAMES = [
|
||||
{ offset: 0, opacity: 0.99, transform: `translate3d(0, ${largeTitleBox.top - 2}px, 0) scale(1)` },
|
||||
{ offset: 0, opacity: 0.99, transform: `translate3d(0, ${largeTitleBox.top + 2}px, 0) scale(1)` },
|
||||
{ offset: 0.6, opacity: 0 },
|
||||
{ offset: 1, opacity: 0, transform: `translate3d(${START_TRANSLATE}, ${backButtonBox.top - 4}px, 0) scale(0.5)` },
|
||||
];
|
||||
@@ -253,10 +253,10 @@ const animateLargeTitle = (
|
||||
[ORIGIN_X]: TITLE_START_OFFSET,
|
||||
})
|
||||
.beforeAddWrite(() => {
|
||||
largeTitleEl.style.setProperty('display', 'none');
|
||||
largeTitleEl.style.setProperty('opacity', '0');
|
||||
})
|
||||
.afterAddWrite(() => {
|
||||
largeTitleEl.style.setProperty('display', '');
|
||||
largeTitleEl.style.setProperty('opacity', '');
|
||||
clonedTitleEl.style.setProperty('display', 'none');
|
||||
})
|
||||
.keyframes(KEYFRAMES);
|
||||
|
||||