fix(checkbox): separate background from svg in order to use proper sizes for ionic theme (#29623)
Issue number: internal --------- ## What is the current behavior? The checkbox background color and svg are both set to the same size. ## What is the new behavior? Moves the background and border to the native wrapper so that we can change the svg size separately. ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information [Preview](https://ionic-framework-git-fix-checkbox-design-ionic1.vercel.app/src/components/checkbox/test/basic?ionic:theme=ionic)
@ -8,7 +8,8 @@
|
|||||||
--border-width: #{globals.$ionic-border-size-025};
|
--border-width: #{globals.$ionic-border-size-025};
|
||||||
--border-style: #{globals.$ionic-border-style-solid};
|
--border-style: #{globals.$ionic-border-style-solid};
|
||||||
--border-color: #{globals.$ionic-color-neutral-800};
|
--border-color: #{globals.$ionic-color-neutral-800};
|
||||||
--checkmark-width: 3;
|
--checkmark-width: #{globals.$ionic-scale-400};
|
||||||
|
--checkmark-height: var(--checkmark-width);
|
||||||
|
|
||||||
// Focus
|
// Focus
|
||||||
--focus-ring-color: #{globals.$ionic-state-focus-1};
|
--focus-ring-color: #{globals.$ionic-state-focus-1};
|
||||||
@ -124,15 +125,14 @@ input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.native-wrapper {
|
.native-wrapper {
|
||||||
|
@include globals.border-radius(var(--border-radius));
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
align-items: center;
|
flex-shrink: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-icon {
|
align-items: center;
|
||||||
@include globals.border-radius(var(--border-radius));
|
justify-content: center;
|
||||||
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
width: var(--size);
|
width: var(--size);
|
||||||
height: var(--size);
|
height: var(--size);
|
||||||
@ -148,6 +148,13 @@ input {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox-icon {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
width: var(--checkmark-width);
|
||||||
|
height: var(--checkmark-height);
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox-icon path {
|
.checkbox-icon path {
|
||||||
fill: var(--checkmark-color);
|
fill: var(--checkmark-color);
|
||||||
|
|
||||||
@ -289,8 +296,8 @@ input {
|
|||||||
// Checked / Indeterminate Checkbox
|
// Checked / Indeterminate Checkbox
|
||||||
// ---------------------------------------------
|
// ---------------------------------------------
|
||||||
|
|
||||||
:host(.checkbox-checked) .checkbox-icon,
|
:host(.checkbox-checked) .native-wrapper,
|
||||||
:host(.checkbox-indeterminate) .checkbox-icon {
|
:host(.checkbox-indeterminate) .native-wrapper {
|
||||||
border-color: var(--border-color-checked);
|
border-color: var(--border-color-checked);
|
||||||
|
|
||||||
background: var(--checkbox-background-checked);
|
background: var(--checkbox-background-checked);
|
||||||
@ -306,7 +313,7 @@ input {
|
|||||||
:host(.ion-invalid) {
|
:host(.ion-invalid) {
|
||||||
--focus-ring-color: #{globals.$ionic-state-focus-2};
|
--focus-ring-color: #{globals.$ionic-state-focus-2};
|
||||||
|
|
||||||
.checkbox-icon {
|
.native-wrapper {
|
||||||
border-color: globals.$ionic-color-danger-800;
|
border-color: globals.$ionic-color-danger-800;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -317,19 +324,28 @@ input {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checkbox overrides the disabled state mixin properties
|
||||||
|
// to fix positioning issues, as the top and left properties
|
||||||
|
// cause the overlay to start inside the border. We unset
|
||||||
|
// these position properties and inherit width and height to
|
||||||
|
// ensure the border is covered.
|
||||||
:host(.checkbox-disabled) .native-wrapper:after {
|
:host(.checkbox-disabled) .native-wrapper:after {
|
||||||
@include globals.disabled-state();
|
@include globals.disabled-state();
|
||||||
|
@include globals.position(unset, unset, unset, unset);
|
||||||
|
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// disabled, unchecked checkbox
|
// disabled, unchecked checkbox
|
||||||
:host(.checkbox-disabled) .checkbox-icon {
|
:host(.checkbox-disabled) .native-wrapper {
|
||||||
border-color: globals.$ionic-color-neutral-800;
|
border-color: globals.$ionic-color-neutral-800;
|
||||||
|
|
||||||
background-color: globals.$ionic-state-disabled;
|
background-color: globals.$ionic-state-disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
// disabled, checked checkbox
|
// disabled, checked checkbox
|
||||||
:host(.checkbox-disabled.checkbox-checked) .checkbox-icon {
|
:host(.checkbox-disabled.checkbox-checked) .native-wrapper {
|
||||||
border-width: globals.$ionic-border-size-0;
|
border-width: globals.$ionic-border-size-0;
|
||||||
|
|
||||||
background-color: globals.$ionic-color-primary-base;
|
background-color: globals.$ionic-color-primary-base;
|
||||||
@ -338,12 +354,12 @@ input {
|
|||||||
// Checkbox Hover
|
// Checkbox Hover
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
@media (any-hover: hover) {
|
@media (any-hover: hover) {
|
||||||
:host(:hover) .checkbox-icon {
|
:host(:hover) .native-wrapper {
|
||||||
background-color: globals.$ionic-color-neutral-100;
|
background-color: globals.$ionic-color-neutral-100;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host(:hover.checkbox-checked) .checkbox-icon,
|
:host(:hover.checkbox-checked) .native-wrapper,
|
||||||
:host(:hover.checkbox-indeterminate) .checkbox-icon {
|
:host(:hover.checkbox-indeterminate) .native-wrapper {
|
||||||
background-color: globals.$ionic-color-primary-800;
|
background-color: globals.$ionic-color-primary-800;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -351,19 +367,19 @@ input {
|
|||||||
// Checkbox Focus
|
// Checkbox Focus
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
// Only show the focus ring when the checkbox is focused and not disabled
|
// Only show the focus ring when the checkbox is focused and not disabled
|
||||||
:host(.ion-focused:not(.checkbox-disabled)) .checkbox-icon {
|
:host(.ion-focused:not(.checkbox-disabled)) .native-wrapper {
|
||||||
outline: var(--focus-ring-width) globals.$ionic-border-style-solid var(--focus-ring-color);
|
outline: var(--focus-ring-width) globals.$ionic-border-style-solid var(--focus-ring-color);
|
||||||
outline-offset: var(--focus-ring-offset);
|
outline-offset: var(--focus-ring-offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checkbox: Active
|
// Checkbox: Active
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
:host(.ion-activated) .checkbox-icon {
|
:host(.ion-activated) .native-wrapper {
|
||||||
background-color: globals.$ionic-color-neutral-200;
|
background-color: globals.$ionic-color-neutral-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host(.ion-activated.checkbox-checked) .checkbox-icon,
|
:host(.ion-activated.checkbox-checked) .native-wrapper,
|
||||||
:host(.ion-activated.checkbox-indeterminate) .checkbox-icon {
|
:host(.ion-activated.checkbox-indeterminate) .native-wrapper {
|
||||||
background-color: globals.$ionic-color-primary-900;
|
background-color: globals.$ionic-color-primary-900;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,18 @@ configs({ modes: ['ios', 'md', 'ionic-md'] }).forEach(({ title, screenshot, conf
|
|||||||
test('should render custom checkmark-width correctly', async ({ page }) => {
|
test('should render custom checkmark-width correctly', async ({ page }) => {
|
||||||
await page.setContent(
|
await page.setContent(
|
||||||
`
|
`
|
||||||
<ion-checkbox checked style="--checkmark-width: 7">Checkmark Width</ion-checkbox>
|
<style>
|
||||||
|
ion-checkbox {
|
||||||
|
--checkmark-width: 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The ionic theme sets the width of the svg not stroke-width */
|
||||||
|
ion-checkbox.ionic {
|
||||||
|
--checkmark-width: 22px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<ion-checkbox checked>Checkmark Width</ion-checkbox>
|
||||||
`,
|
`,
|
||||||
config
|
config
|
||||||
);
|
);
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.5 KiB |
@ -23,6 +23,15 @@
|
|||||||
transform: scale(0.5);
|
transform: scale(0.5);
|
||||||
transform-origin: center;
|
transform-origin: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkmark-width {
|
||||||
|
--checkmark-width: 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The ionic theme sets the width of the svg not stroke-width */
|
||||||
|
.checkmark-width.ionic {
|
||||||
|
--checkmark-width: 22px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<body>
|
<body>
|
||||||
<ion-app>
|
<ion-app>
|
||||||
@ -38,7 +47,10 @@
|
|||||||
<ion-checkbox justify="start" checked>Checked</ion-checkbox>
|
<ion-checkbox justify="start" checked>Checked</ion-checkbox>
|
||||||
<ion-checkbox justify="start" disabled>Disabled</ion-checkbox>
|
<ion-checkbox justify="start" disabled>Disabled</ion-checkbox>
|
||||||
<ion-checkbox justify="start" disabled checked>Disabled, Checked</ion-checkbox>
|
<ion-checkbox justify="start" disabled checked>Disabled, Checked</ion-checkbox>
|
||||||
<ion-checkbox justify="start" checked style="--checkmark-width: 7">Checkmark Width</ion-checkbox>
|
<ion-checkbox justify="start" disabled style="--border-width: 3px"
|
||||||
|
>Disabled, Custom Border Width</ion-checkbox
|
||||||
|
>
|
||||||
|
<ion-checkbox justify="start" checked class="checkmark-width">Checkmark Width</ion-checkbox>
|
||||||
<ion-checkbox justify="start" checked class="checkbox-part">Checkmark Shadow Part</ion-checkbox>
|
<ion-checkbox justify="start" checked class="checkbox-part">Checkmark Shadow Part</ion-checkbox>
|
||||||
<ion-checkbox justify="start" checked style="--size: 100px">--size</ion-checkbox>
|
<ion-checkbox justify="start" checked style="--size: 100px">--size</ion-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
@ -71,6 +71,16 @@
|
|||||||
<h2>Disabled, Checked</h2>
|
<h2>Disabled, Checked</h2>
|
||||||
<ion-checkbox disabled="true" checked>Enable Notifications</ion-checkbox>
|
<ion-checkbox disabled="true" checked>Enable Notifications</ion-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-item">
|
||||||
|
<h2>Focused, Unchecked</h2>
|
||||||
|
<ion-checkbox class="ion-focused">Enable Notifications</ion-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-item">
|
||||||
|
<h2>Focused, Checked</h2>
|
||||||
|
<ion-checkbox checked class="ion-focused">Enable Notifications</ion-checkbox>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
</ion-app>
|
</ion-app>
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
@mixin disabled-state() {
|
@mixin disabled-state() {
|
||||||
@include mixins.position(0, 0, 0, 0);
|
@include mixins.position(0, 0, 0, 0);
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
background-color: tokens.$ionic-state-disabled;
|
background-color: tokens.$ionic-state-disabled;
|
||||||
|
|||||||