Skip to content

Commit

Permalink
feat(theme): improved color contrast with color palette (#28791)
Browse files Browse the repository at this point in the history
Issue number: Internal

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

The team would like to ensure that Ionic Framework components that use
an Ionic color (primary, secondary, etc) on top of a contrast color pass
minimum contrast ratios as defined in the WCAG.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Introduces a revised set of Ionic colors that pass AA color contrast
guidelines when with the appropriate contrast.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

---------

Co-authored-by: Sean Perkins <[email protected]>
Co-authored-by: Brandy Carney <[email protected]>
Co-authored-by: Sean Perkins <[email protected]>
Co-authored-by: Shawn Taylor <[email protected]>
Co-authored-by: Brandy Carney <[email protected]>
Co-authored-by: Amanda Johnston <[email protected]>
Co-authored-by: Maria Hutt <[email protected]>
Co-authored-by: ionitron <[email protected]>
  • Loading branch information
9 people authored Jan 8, 2024
1 parent 2c20a6b commit 15e368c
Show file tree
Hide file tree
Showing 3,123 changed files with 1,762 additions and 747 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
16 changes: 15 additions & 1 deletion BREAKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This is a comprehensive list of the breaking changes introduced in the major ver
## Version 8.x

- [Browser and Platform Support](#version-8x-browser-platform-support)
- [Global Styles](#global-styles)
- [Components](#version-8x-components)
- [Button](#version-8x-button)
- [Content](#version-8x-content)
Expand Down Expand Up @@ -45,6 +46,18 @@ This section details the desktop browser, JavaScript framework, and mobile platf
| iOS | 15+ |
| Android | 5.1+ with Chromium 89+ |

<h2 id="version-8x-global-styles">Global Styles</h2>

The `core.css` file has been updated to set the text color on the `body` element:

```diff
body {
+ color: var(--ion-text-color);
}
```

This allows components to inherit the color properly when used outside of Ionic Framework and is required for custom themes to work properly. However, it may have unintentional side effects in apps if the color was not expected to inherit.

<h2 id="version-8x-components">Components</h2>

<h4 id="version-8x-button">Button</h4>
Expand All @@ -66,8 +79,9 @@ This section details the desktop browser, JavaScript framework, and mobile platf
+ background: red;
}
```

<h4 id="version-8x-picker">Picker</h4>

- `ion-picker` and `ion-picker-column` have been renamed to `ion-picker-legacy` and `ion-picker-legacy-column`, respectively. This change was made to accommodate the new inline picker component while allowing developers to continue to use the legacy picker during this migration period.
- Only the component names have been changed. Usages such as `ion-picker` or `IonPicker` should be changed to `ion-picker-legacy` and `IonPickerLegacy`, respectively.
- Non-component usages such as `pickerController` or `useIonPicker` remain unchanged. The new picker displays inline with your page content and does not have equivalents for these non-component usages.
- Non-component usages such as `pickerController` or `useIonPicker` remain unchanged. The new picker displays inline with your page content and does not have equivalents for these non-component usages.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 30 additions & 16 deletions core/src/components/action-sheet/test/a11y/action-sheet.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,40 @@ const testAriaButton = async (
await expect(actionSheetButton).toHaveAttribute('aria-label', expectedAriaLabel);
};

configs({ directions: ['ltr'], themes: ['dark', 'light'] }).forEach(({ config, title }) => {
test.describe(title('action-sheet: Axe testing'), () => {
test('should not have accessibility violations when header is defined', async ({ page }) => {
await page.setContent(
`
<ion-action-sheet></ion-action-sheet>
<script>
const actionSheet = document.querySelector('ion-action-sheet');
actionSheet.header = 'Header';
actionSheet.subHeader = 'Subtitle';
actionSheet.buttons = ['Confirm'];
</script>
`,
config
);

const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');
const actionSheet = page.locator('ion-action-sheet');

await actionSheet.evaluate((el: HTMLIonActionSheetElement) => el.present());
await ionActionSheetDidPresent.next();

const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});
});
});

configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
test.describe(title('action-sheet: a11y'), () => {
test.describe(title('action-sheet: aria attributes'), () => {
test.beforeEach(async ({ page }) => {
await page.goto(`/src/components/action-sheet/test/a11y`, config);
});
test('should not have accessibility violations when header is defined', async ({ page }) => {
const button = page.locator('#bothHeaders');
const didPresent = await page.spyOnEvent('ionActionSheetDidPresent');

await button.click();
await didPresent.next();

/**
* action-sheet overlays the entire screen, so
* Axe will be unable to verify color contrast
* on elements under the backdrop.
*/
const results = await new AxeBuilder({ page }).disableRules('color-contrast').analyze();
expect(results.violations).toEqual([]);
});

test('should have aria-labelledby when header is set', async ({ page }) => {
await testAria(page, 'bothHeaders', 'action-sheet-1-header');
Expand Down
40 changes: 29 additions & 11 deletions core/src/components/alert/test/a11y/alert.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,35 @@ const testAria = async (
expect(ariaDescribedBy).toBe(expectedAriaDescribedBy);
};

configs({ directions: ['ltr'], themes: ['dark', 'light'] }).forEach(({ title, config }) => {
test.describe(title('alert: Axe testing'), () => {
test('should not have accessibility violations when header and message are defined', async ({ page }) => {
await page.setContent(
`
<ion-alert></ion-alert>
<script>
const alert = document.querySelector('ion-alert');
alert.header = 'Header';
alert.subHeader = 'Subtitle';
alert.buttons = ['OK'];
</script>
`,
config
);

const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
const alert = page.locator('ion-alert');

await alert.evaluate((el: HTMLIonAlertElement) => el.present());
await ionAlertDidPresent.next();

const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});
});
});

configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('alert: text wrapping'), () => {
test('should break on words and white spaces for radios', async ({ page }, testInfo) => {
Expand Down Expand Up @@ -94,17 +123,6 @@ configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
test.beforeEach(async ({ page }) => {
await page.goto(`/src/components/alert/test/a11y`, config);
});
test('should not have accessibility violations when header and message are defined', async ({ page }) => {
const didPresent = await page.spyOnEvent('ionAlertDidPresent');
const button = page.locator('#bothHeaders');
await button.click();

await didPresent.next();

// TODO FW-4375
const results = await new AxeBuilder({ page }).disableRules('color-contrast').analyze();
expect(results.violations).toEqual([]);
});

test('should have aria-labelledby when header is set', async ({ page }) => {
await testAria(page, 'noMessage', 'alert-1-hdr', null);
Expand Down
26 changes: 26 additions & 0 deletions core/src/components/back-button/test/a11y/back-button.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
import AxeBuilder from '@axe-core/playwright';
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';

/**
* Only ios mode uses ion-color() for the back button
*/
configs({ directions: ['ltr'], modes: ['ios'], themes: ['light', 'dark'] }).forEach(({ title, config }) => {
test.describe(title('back-button: a11y for ion-color()'), () => {
test('should not have accessibility violations', async ({ page }) => {
await page.setContent(
`
<style>
ion-back-button {
display: inline-block !important;
vertical-align: middle;
}
</style>
<ion-back-button text="Back" aria-label="back button"></ion-back-button>
`,
config
);

const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});
});
});

configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('back-button: font scaling'), () => {
test('should scale text on larger font sizes', async ({ page }) => {
Expand Down
24 changes: 24 additions & 0 deletions core/src/components/badge/test/a11y/badge.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import AxeBuilder from '@axe-core/playwright';
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';

Expand All @@ -23,3 +24,26 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
});
});
});

configs({ directions: ['ltr'], themes: ['light', 'dark'] }).forEach(({ config, title }) => {
test.describe(title('badge: a11y'), () => {
test('should not have accessibility violations', async ({ page }) => {
/**
* All page content should be contained by landmarks (main, nav, etc.)
* By containing the badge in a main element, we can avoid this violation.
*/
await page.setContent(
`
<main>
<ion-badge>123</ion-badge>
</main>
`,
config
);

const results = await new AxeBuilder({ page }).analyze();

expect(results.violations).toEqual([]);
});
});
});
Loading

0 comments on commit 15e368c

Please sign in to comment.