Skip to content

Commit

Permalink
fix(dark-palette): improve base colors (#29239)
Browse files Browse the repository at this point in the history
Issue number: resolves #29219

---------

<!-- 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. -->

We got feedback that the current dark theme is a bit jarring when
compared with native. In particular, devs are expecting the contrast to
be white for many of these colors to match native iOS. Currently, text
inside of a primary button is black on dark mode, but devs expect it to
be white. Additionally, the dark mode colors appear to be too washed out
when compared with their light mode counterparts.

The team discussed this and we think we can find a way to make the
colors more in line with what devs expect while still allowing for AA
color contrast levels.

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

- Adjusted the secondary and danger colors to be slightly more
vibrant/inline with what developers expect in mobile apps while still
meeting AA color 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

I intentionally did not change the contrast color, and I intentionally
did not significantly change the vibrancy of each color token. I did
some research into how the native platforms handle colors. I'll place
this information here, because there are some subtle differences between
web and native that make a difference.

### Material Design


[Source](https://m2.material.io/design/color/dark-theme.html#ui-application)

Material Design 2 calls for desaturated color tokens as well as flipping
contrast colors. For example, a vibrant purple background with light
text on light mode is a washed purple with dark text on dark mode. The
colors in Ionic <=7 did not follow these patterns. The tokens in Ionic 8
now follows these patterns, so what we have in `main` means we are
aligning closer with native MD than have in the past.


![image](https://github.com/ionic-team/ionic-framework/assets/2721089/b1067e7c-ee51-4170-91b1-27c0753fae72)

### iOS


[Source](https://developer.apple.com/design/human-interface-guidelines/accessibility#Color-and-effects)

iOS is a bit tricky. On the docs, Apple references the WCAG color
contrast formula (what Ionic follows). However, the table they present
below is slightly different:

| Text Size | Text Weight | Minimum Contrast Ratio |
| - | - | - |
| Up to 17 points | All | 4.5:1 |
| 18 points and larger | All | 3:1 |
| All | Bold | 3:1 |

The last row is the main difference. WCAG states that text that is bold
AND >=14pts (~18.66px) needs to meet a minimum contrast ratio of 3:1.
Apple's guidelines state that any text that is bold (regardless of size)
needs to meet a minimum contrast ratio of 3:1. In other words, **Apple
is using different guidelines to choose colors which is why colored dark
mode buttons on iOS typically use white text**.

However, Apple is inconsistent in implementing its own guidelines.
Consider the following red buttons in the Apple Music app.

| Screenshot | Meets Web guidelines | Meets Apple guidelines | Notes |
| - | - | - | - |
| <img
src="https://github.com/ionic-team/ionic-framework/assets/2721089/6bb0e0b3-c29e-4b96-9ad5-e45e59bf0415"
width="300" /> | ❌ | ❌ | Text is not bold which makes it not meet either
guidelines. |
| <img
src="https://github.com/ionic-team/ionic-framework/assets/2721089/8ce93b45-0b2b-4160-97a4-19852134684c"
width="300" /> | ❌ | ✅ | Text is bold which makes it pass the Apple
guidelines, but it's still too small to also pass the Web guidelines. |

### Ionic

One of the things I tried is adjusting the base color to work well with
white and black backgrounds. It's common to have a blue background with
white text (such as a button) AND to have a blue link on a black
background (such as an `a` element). This approach does not work well
for the shade/tint colors used for hover/focus states. These colors also
need to meet Web guidelines.

Consider the following example:

| Screenshot | Notes | Text/Link passes AA guidelines | Tint passes AA
guidelines |
| - | - | - | - |
| ![Screenshot 2024-04-15 at 3 28
47 PM](https://github.com/ionic-team/ionic-framework/assets/2721089/3d8c56a6-6d0c-4c93-bc13-9c93508f76bc)
| This uses the same primary color found in the light palette | ❌ | ✅ |
| ![Screenshot 2024-04-15 at 3 29
21 PM](https://github.com/ionic-team/ionic-framework/assets/2721089/beaf26c9-c30f-4a39-900e-74b0f2433280)
| This changes the base color such that the Text/Link passes with a
4.5:1 ratio | ✅ | ❌ |
| ![Screenshot 2024-04-15 at 3 34
14 PM](https://github.com/ionic-team/ionic-framework/assets/2721089/43dbd81c-1338-4fff-af30-efbe51fce08a)
| This changes the base color such that the Tint passes with a 4:5:1
ratio | ❌ | ✅ |

We also considered adjusting the `a` and `ion-text` colors to use the
tint color. We decided against that because it a) felt a little odd
given that tint is typically used for states (hover, focus, etc) and b)
we were concerned that making this change would impose future
restrictions on how this color palette can evolve.

### Conclusion

While Ionic's colors don't exactly match the iOS colors, the reality is
that we are a web-based tool, so we are going to be evaluated using
web-based tools/standards. As a result, it's recommended that we align
closer with the Web guidelines than Apple's guidelines.

## Testing

Testing:

Developers can look at the colors by opening
http://localhost:3333/src/themes/test/colors

In the video below, the first state is the dark theme in `main`. The
second state is the proposed changes.
| Visual Comparison |
| - |
| <video
src="https://github.com/ionic-team/ionic-framework/assets/2721089/241fb1b6-212b-4f03-83cd-9558b9a53761"></video>
|
  • Loading branch information
liamdebeasi committed Apr 15, 2024
1 parent 9b59138 commit 4698d22
Showing 1 changed file with 78 additions and 54 deletions.
132 changes: 78 additions & 54 deletions core/src/css/palettes/dark.scss
Original file line number Diff line number Diff line change
@@ -1,59 +1,83 @@
@use "sass:map";
@import "../../themes/ionic.functions.color";

$primary: #4d8dff;
$secondary: #46b1ff;
$tertiary: #8482fb;
$success: #2dd55b;
$warning: #ffce31;
$danger: #f24c58;
$light: #222428;
$medium: #989aa2;
$dark: #f4f5f8;

$colors: (
primary: (
base: $primary,
contrast: #000,
shade: get-color-shade($primary),
tint: get-color-tint($primary)
),
secondary: (
base: $secondary,
contrast: #000,
shade: get-color-shade($secondary),
tint: get-color-tint($secondary)
),
tertiary: (
base: $tertiary,
contrast: #000,
shade: get-color-shade($tertiary),
tint: get-color-tint($tertiary)
),
success: (
base: $success,
contrast: #000,
shade: get-color-shade($success),
tint: get-color-tint($success)
),
warning: (
base: $warning,
contrast: #000,
shade: get-color-shade($warning),
tint: get-color-tint($warning)
),
danger: (
base: $danger,
contrast: #000,
shade: get-color-shade($danger),
tint: get-color-tint($danger)
),
light: (
base: $light,
contrast: #fff,
shade: get-color-shade($light),
tint: get-color-tint($light)
),
medium: (
base: $medium,
contrast: #000,
shade: get-color-shade($medium),
tint: get-color-tint($medium)
),
dark: (
base: $dark,
contrast: #000,
shade: get-color-shade($dark),
tint: get-color-tint($dark)
)
);

@mixin dark-base-palette() {
& {
--ion-color-primary: #4d8dff;
--ion-color-primary-rgb: 77, 141, 255;
--ion-color-primary-contrast: #000000;
--ion-color-primary-contrast-rgb: 0, 0, 0;
--ion-color-primary-shade: #447ce0;
--ion-color-primary-tint: #5f98ff;
--ion-color-secondary: #62bdff;
--ion-color-secondary-rgb: 98, 189, 255;
--ion-color-secondary-contrast: #000000;
--ion-color-secondary-contrast-rgb: 0, 0, 0;
--ion-color-secondary-shade: #56a6e0;
--ion-color-secondary-tint: #72c4ff;
--ion-color-tertiary: #8482fb;
--ion-color-tertiary-rgb: 132, 130, 251;
--ion-color-tertiary-contrast: #000000;
--ion-color-tertiary-contrast-rgb: 0, 0, 0;
--ion-color-tertiary-shade: #7472dd;
--ion-color-tertiary-tint: #908ffb;
--ion-color-success: #2dd55b;
--ion-color-success-rgb: 45, 213, 91;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0, 0, 0;
--ion-color-success-shade: #28bb50;
--ion-color-success-tint: #42d96b;
--ion-color-warning: #ffce31;
--ion-color-warning-rgb: 255, 206, 49;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0b52b;
--ion-color-warning-tint: #ffd346;
--ion-color-danger: #f56570;
--ion-color-danger-rgb: 245, 101, 112;
--ion-color-danger-contrast: #000000;
--ion-color-danger-contrast-rgb: 0, 0, 0;
--ion-color-danger-shade: #d85963;
--ion-color-danger-tint: #f6747e;
--ion-color-dark: #f4f5f8;
--ion-color-dark-rgb: 244, 245, 248;
--ion-color-dark-contrast: #000000;
--ion-color-dark-contrast-rgb: 0, 0, 0;
--ion-color-dark-shade: #d7d8da;
--ion-color-dark-tint: #f5f6f9;
--ion-color-medium: #989aa2;
--ion-color-medium-rgb: 152, 154, 162;
--ion-color-medium-contrast: #000000;
--ion-color-medium-contrast-rgb: 0, 0, 0;
--ion-color-medium-shade: #86888f;
--ion-color-medium-tint: #a2a4ab;
--ion-color-light: #222428;
--ion-color-light-rgb: 34, 36, 40;
--ion-color-light-contrast: #ffffff;
--ion-color-light-contrast-rgb: 255, 255, 255;
--ion-color-light-shade: #1e2023;
--ion-color-light-tint: #383a3e;
@each $color-name, $value in $colors {
--ion-color-#{$color-name}: #{map.get($value, base)};
--ion-color-#{$color-name}-rgb: #{color-to-rgb-list(map.get($value, base))};
--ion-color-#{$color-name}-contrast: #{map.get($value, contrast)};
--ion-color-#{$color-name}-contrast-rgb: #{color-to-rgb-list(map.get($value, contrast))};
--ion-color-#{$color-name}-shade: #{map.get($value, shade)};
--ion-color-#{$color-name}-tint: #{map.get($value, tint)};
}
}
}

Expand Down

0 comments on commit 4698d22

Please sign in to comment.