diff --git a/demo/e2e/rtl.spec.ts b/demo/e2e/rtl.spec.ts index b4ec920..e24f0a8 100644 --- a/demo/e2e/rtl.spec.ts +++ b/demo/e2e/rtl.spec.ts @@ -7,6 +7,8 @@ for (const direction of ['ltr', 'rtl'] as const) { const fixture = document.createElement('div'); fixture.id = 'rtl-range-probe'; fixture.dir = dir; + fixture.style.width = '200px'; + fixture.style.position = 'relative'; const range = document.createElement('ion-range'); range.id = 'single-range'; @@ -30,28 +32,22 @@ for (const direction of ['ltr', 'rtl'] as const) { await expect(page.locator('#single-range')).toHaveClass(/hydrated/); await expect(page.locator('#dual-range')).toHaveClass(/hydrated/); - const normalOffset = await page.locator('#single-range').evaluate((range) => { - const knob = range.shadowRoot!.querySelector('[part~="knob"]')!; - return new DOMMatrixReadOnly(getComputedStyle(knob).transform).e; - }); - expect(normalOffset).toBeCloseTo(direction === 'ltr' ? 20 : -20, 1); - - await page.locator('#single-range').evaluate((range) => range.classList.add('range-pressed')); - await expect - .poll(() => - page.locator('#single-range').evaluate((range) => { - const knob = range.shadowRoot!.querySelector('[part~="knob"]')!; - return new DOMMatrixReadOnly(getComputedStyle(knob).transform).e; - }), - ) - .toBeCloseTo(direction === 'ltr' ? 21 : -21, 1); + // The knob handle is positioned from the inline-start edge by the value + // ratio, so LTR uses left: 50% and RTL uses right: 50%. + const handleSide = direction === 'ltr' ? 'left' : 'right'; + const handlePosition = await page.locator('#single-range').evaluate((range, side) => { + const handle = range.shadowRoot!.querySelector('[part~="knob-handle"]')!; + return getComputedStyle(handle)[side]; + }, handleSide); + expect(handlePosition).toBe('100px'); + // With the default Ionic range geometry the bar has no extra inline margins. const dualMargins = await page.locator('#dual-range').evaluate((range) => { const bar = range.shadowRoot!.querySelector('[part~="bar"]')!; const style = getComputedStyle(bar); return { start: style.marginInlineStart, end: style.marginInlineEnd }; }); - expect(dualMargins).toEqual({ start: '6px', end: '6px' }); + expect(dualMargins).toEqual({ start: '0px', end: '0px' }); }); test(`${direction} mirrors Material forward and back transitions`, async ({ page }) => { diff --git a/demo/e2e/screenshot.spec.ts-snapshots/accordion-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/accordion-dark.png index 7a973c5..e699e56 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/accordion-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/accordion-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/accordion.png b/demo/e2e/screenshot.spec.ts-snapshots/accordion.png index 665ef5b..11da6a9 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/accordion.png and b/demo/e2e/screenshot.spec.ts-snapshots/accordion.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-all-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-all-dark.png index a319c5e..b9f53ea 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-all-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-all-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-all.png b/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-all.png index 67245b0..ea05586 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-all.png and b/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-all.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-button-only-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-button-only-dark.png index 7916af5..6635c8c 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-button-only-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-button-only-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-button-only.png b/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-button-only.png index 14c9d07..89f963c 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-button-only.png and b/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-button-only.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-no-cancel-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-no-cancel-dark.png index 6268406..6139927 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-no-cancel-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-no-cancel-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-no-cancel.png b/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-no-cancel.png index d84ad0f..48d5a35 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-no-cancel.png and b/demo/e2e/screenshot.spec.ts-snapshots/action-sheet-no-cancel.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/alert-all-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/alert-all-dark.png index 6559021..62587a5 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/alert-all-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/alert-all-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/alert-all.png b/demo/e2e/screenshot.spec.ts-snapshots/alert-all.png index 693132c..b619936 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/alert-all.png and b/demo/e2e/screenshot.spec.ts-snapshots/alert-all.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/alert-button-only-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/alert-button-only-dark.png index c5cd7b4..05f7cb2 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/alert-button-only-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/alert-button-only-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/alert-button-only.png b/demo/e2e/screenshot.spec.ts-snapshots/alert-button-only.png index 640d480..bda539e 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/alert-button-only.png and b/demo/e2e/screenshot.spec.ts-snapshots/alert-button-only.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/alert-no-cancel-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/alert-no-cancel-dark.png index ea67e31..31fa03c 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/alert-no-cancel-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/alert-no-cancel-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/alert-no-cancel.png b/demo/e2e/screenshot.spec.ts-snapshots/alert-no-cancel.png index bc38aea..eee8f73 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/alert-no-cancel.png and b/demo/e2e/screenshot.spec.ts-snapshots/alert-no-cancel.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/alert-remove-app-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/alert-remove-app-dark.png index df4b13e..3948586 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/alert-remove-app-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/alert-remove-app-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/alert-remove-app.png b/demo/e2e/screenshot.spec.ts-snapshots/alert-remove-app.png index 15463d8..9384773 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/alert-remove-app.png and b/demo/e2e/screenshot.spec.ts-snapshots/alert-remove-app.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/breadcrumbs-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/breadcrumbs-dark.png index 85ac6e3..aa017fb 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/breadcrumbs-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/breadcrumbs-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/breadcrumbs.png b/demo/e2e/screenshot.spec.ts-snapshots/breadcrumbs.png index ff207e3..6212171 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/breadcrumbs.png and b/demo/e2e/screenshot.spec.ts-snapshots/breadcrumbs.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/button-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/button-dark.png index a59f26a..a342260 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/button-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/button-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/button.png b/demo/e2e/screenshot.spec.ts-snapshots/button.png index 42a878c..dc642ec 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/button.png and b/demo/e2e/screenshot.spec.ts-snapshots/button.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/card-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/card-dark.png index 1f66df1..7a20a47 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/card-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/card-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/card.png b/demo/e2e/screenshot.spec.ts-snapshots/card.png index 585de63..5e01e08 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/card.png and b/demo/e2e/screenshot.spec.ts-snapshots/card.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/checkbox-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/checkbox-dark.png index b107ab5..6e315c1 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/checkbox-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/checkbox-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/checkbox.png b/demo/e2e/screenshot.spec.ts-snapshots/checkbox.png index 34e3f88..05d30c0 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/checkbox.png and b/demo/e2e/screenshot.spec.ts-snapshots/checkbox.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/chip-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/chip-dark.png index 3289737..3e862b9 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/chip-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/chip-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/chip.png b/demo/e2e/screenshot.spec.ts-snapshots/chip.png index 0bafa07..f723d02 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/chip.png and b/demo/e2e/screenshot.spec.ts-snapshots/chip.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/date-and-time-pickers-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/date-and-time-pickers-dark.png index 7443007..5355522 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/date-and-time-pickers-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/date-and-time-pickers-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/date-and-time-pickers.png b/demo/e2e/screenshot.spec.ts-snapshots/date-and-time-pickers.png index 0210ede..ff29d08 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/date-and-time-pickers.png and b/demo/e2e/screenshot.spec.ts-snapshots/date-and-time-pickers.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/floating-action-button-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/floating-action-button-dark.png index 9f58f18..35c6de4 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/floating-action-button-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/floating-action-button-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/floating-action-button.png b/demo/e2e/screenshot.spec.ts-snapshots/floating-action-button.png index 1ef7556..a295316 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/floating-action-button.png and b/demo/e2e/screenshot.spec.ts-snapshots/floating-action-button.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/inputs-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/inputs-dark.png index 2e2ee1c..8ec64c0 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/inputs-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/inputs-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/inputs.png b/demo/e2e/screenshot.spec.ts-snapshots/inputs.png index e7091c5..789d922 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/inputs.png and b/demo/e2e/screenshot.spec.ts-snapshots/inputs.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/item-list-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/item-list-dark.png index 61be0f3..e82ee2c 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/item-list-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/item-list-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/item-list.png b/demo/e2e/screenshot.spec.ts-snapshots/item-list.png index f9ef633..9469bb1 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/item-list.png and b/demo/e2e/screenshot.spec.ts-snapshots/item-list.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/modal-card-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/modal-card-dark.png index a6ccb10..2a632a6 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/modal-card-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/modal-card-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/modal-card.png b/demo/e2e/screenshot.spec.ts-snapshots/modal-card.png index c7ec271..8ee6c67 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/modal-card.png and b/demo/e2e/screenshot.spec.ts-snapshots/modal-card.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/modal-normal-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/modal-normal-dark.png index a6ccb10..2a632a6 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/modal-normal-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/modal-normal-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/modal-normal.png b/demo/e2e/screenshot.spec.ts-snapshots/modal-normal.png index c7ec271..8ee6c67 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/modal-normal.png and b/demo/e2e/screenshot.spec.ts-snapshots/modal-normal.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/popover-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/popover-dark.png index 6294e4f..e146e2d 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/popover-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/popover-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/popover.png b/demo/e2e/screenshot.spec.ts-snapshots/popover.png index 8d15931..470b1b0 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/popover.png and b/demo/e2e/screenshot.spec.ts-snapshots/popover.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-icon-only-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-icon-only-dark.png index 4f0d1fb..65e75e5 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-icon-only-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-icon-only-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-icon-only.png b/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-icon-only.png index 9f22bc0..76064db 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-icon-only.png and b/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-icon-only.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-message-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-message-dark.png index 8bb4cf1..3fba8b7 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-message-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-message-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-message.png b/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-message.png index 2bf3ccb..2c6470a 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-message.png and b/demo/e2e/screenshot.spec.ts-snapshots/progress-indicators-message.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/radio-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/radio-dark.png index 81f61c7..00d69b4 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/radio-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/radio-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/radio.png b/demo/e2e/screenshot.spec.ts-snapshots/radio.png index fd65a67..e4f2df2 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/radio.png and b/demo/e2e/screenshot.spec.ts-snapshots/radio.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/range-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/range-dark.png index 952935c..b7a90eb 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/range-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/range-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/range.png b/demo/e2e/screenshot.spec.ts-snapshots/range.png index dadadc5..41e0961 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/range.png and b/demo/e2e/screenshot.spec.ts-snapshots/range.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/reorder-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/reorder-dark.png index 82c951f..e24eedd 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/reorder-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/reorder-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/reorder.png b/demo/e2e/screenshot.spec.ts-snapshots/reorder.png index 7885b94..b9763fe 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/reorder.png and b/demo/e2e/screenshot.spec.ts-snapshots/reorder.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/searchbar-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/searchbar-dark.png index e959e4e..b2e3619 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/searchbar-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/searchbar-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/searchbar.png b/demo/e2e/screenshot.spec.ts-snapshots/searchbar.png index bc9b9d1..60d7737 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/searchbar.png and b/demo/e2e/screenshot.spec.ts-snapshots/searchbar.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/segment-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/segment-dark.png index ddaa8a1..c42f438 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/segment-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/segment-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/segment.png b/demo/e2e/screenshot.spec.ts-snapshots/segment.png index 91eabcc..5385c45 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/segment.png and b/demo/e2e/screenshot.spec.ts-snapshots/segment.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/select-alert-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/select-alert-dark.png index 31be75a..c70a519 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/select-alert-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/select-alert-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/select-alert.png b/demo/e2e/screenshot.spec.ts-snapshots/select-alert.png index 59712d0..edcbfcc 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/select-alert.png and b/demo/e2e/screenshot.spec.ts-snapshots/select-alert.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/select-modal-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/select-modal-dark.png index b54cf1d..6310f1f 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/select-modal-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/select-modal-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/select-modal.png b/demo/e2e/screenshot.spec.ts-snapshots/select-modal.png index 5114ef0..47741bf 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/select-modal.png and b/demo/e2e/screenshot.spec.ts-snapshots/select-modal.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/select-popover-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/select-popover-dark.png index 1737791..5eecffc 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/select-popover-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/select-popover-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/select-popover.png b/demo/e2e/screenshot.spec.ts-snapshots/select-popover.png index 10a3add..c4619b4 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/select-popover.png and b/demo/e2e/screenshot.spec.ts-snapshots/select-popover.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/tabs-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/tabs-dark.png index 9b8a61b..b2af596 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/tabs-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/tabs-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/tabs.png b/demo/e2e/screenshot.spec.ts-snapshots/tabs.png index c3150d3..bf86182 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/tabs.png and b/demo/e2e/screenshot.spec.ts-snapshots/tabs.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/toast-anchor-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/toast-anchor-dark.png index 821b34f..469bb3c 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/toast-anchor-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/toast-anchor-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/toast-anchor.png b/demo/e2e/screenshot.spec.ts-snapshots/toast-anchor.png index f7b7812..5f052df 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/toast-anchor.png and b/demo/e2e/screenshot.spec.ts-snapshots/toast-anchor.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/toast-middle-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/toast-middle-dark.png index beb7b81..1d14601 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/toast-middle-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/toast-middle-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/toast-middle.png b/demo/e2e/screenshot.spec.ts-snapshots/toast-middle.png index 0948645..fb549a4 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/toast-middle.png and b/demo/e2e/screenshot.spec.ts-snapshots/toast-middle.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/toast-top-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/toast-top-dark.png index d0d92b1..aed9577 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/toast-top-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/toast-top-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/toast-top.png b/demo/e2e/screenshot.spec.ts-snapshots/toast-top.png index 5c5200c..a5c9f5c 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/toast-top.png and b/demo/e2e/screenshot.spec.ts-snapshots/toast-top.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/toggle-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/toggle-dark.png index 19b5e47..cc14ef8 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/toggle-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/toggle-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/toggle.png b/demo/e2e/screenshot.spec.ts-snapshots/toggle.png index 667ee29..a4af7c3 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/toggle.png and b/demo/e2e/screenshot.spec.ts-snapshots/toggle.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/toolbar-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/toolbar-dark.png index b2b008c..302363d 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/toolbar-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/toolbar-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/toolbar.png b/demo/e2e/screenshot.spec.ts-snapshots/toolbar.png index c49c274..f6ddca6 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/toolbar.png and b/demo/e2e/screenshot.spec.ts-snapshots/toolbar.png differ diff --git a/demo/src/app/index/pages/action-sheet/action-sheet.util.ts b/demo/src/app/index/pages/action-sheet/action-sheet.util.ts index 91227db..46c8284 100644 --- a/demo/src/app/index/pages/action-sheet/action-sheet.util.ts +++ b/demo/src/app/index/pages/action-sheet/action-sheet.util.ts @@ -10,6 +10,14 @@ export const actionSheetUtil = () => ({ action: 'delete', }, }, + { + text: 'Confirm', + role: 'preferred', + icon: 'checkmark-outline', + data: { + action: 'confirm', + }, + }, { text: 'Share', icon: 'share-outline', diff --git a/demo/src/app/index/pages/alert/alert.page.ts b/demo/src/app/index/pages/alert/alert.page.ts index 5ee0170..07c6669 100644 --- a/demo/src/app/index/pages/alert/alert.page.ts +++ b/demo/src/app/index/pages/alert/alert.page.ts @@ -75,12 +75,25 @@ export class AlertPage implements OnInit { subHeader: undefined, message: 'Removing from Home Screen will keep the app in your App Library', buttons: [ - ...alertUtil().buttons, + { + text: 'Cancel', + role: 'cancel', + handler: () => { + console.log('Alert canceled'); + }, + }, + { + text: 'Keep', + role: 'preferred', + handler: () => { + console.log('Alert kept'); + }, + }, { text: 'Remove from Home Screen', - role: 'confirm', + role: 'destructive', handler: () => { - console.log('Alert confirmed'); + console.log('Alert removed'); }, }, ], diff --git a/demo/src/app/index/pages/alert/alert.util.ts b/demo/src/app/index/pages/alert/alert.util.ts index 53abadd..504fda5 100644 --- a/demo/src/app/index/pages/alert/alert.util.ts +++ b/demo/src/app/index/pages/alert/alert.util.ts @@ -11,11 +11,18 @@ export const alertUtil = () => ({ }, }, { - text: 'OK', - role: 'destructive', + text: 'Confirm', + role: 'preferred', handler: () => { console.log('Alert confirmed'); }, }, + { + text: 'Delete', + role: 'destructive', + handler: () => { + console.log('Alert deleted'); + }, + }, ], }); diff --git a/src/styles/components/ion-action-sheet.scss b/src/styles/components/ion-action-sheet.scss index e38c8b6..039fe0b 100644 --- a/src/styles/components/ion-action-sheet.scss +++ b/src/styles/components/ion-action-sheet.scss @@ -1,5 +1,7 @@ ion-action-sheet.md:not(.md3-disabled) { --max-width: 640px; + --background: var(--md-sys-color-surface-container-low); + --color: var(--md-sys-color-on-surface); .action-sheet-container { overflow: hidden; @@ -12,6 +14,23 @@ ion-action-sheet.md:not(.md3-disabled) { .action-sheet-button { padding: 12px 24px; + + &.action-sheet-destructive { + --button-color: var(--md-sys-color-error); + --color: var(--md-sys-color-error); + } + + &.action-sheet-preferred { + --button-color: var(--md-sys-color-on-primary); + --color: var(--md-sys-color-on-primary); + --button-background: var(--md-sys-color-primary); + --button-background-activated: var(--md-sys-color-on-primary); + --button-background-activated-opacity: 0.12; + --button-background-hover: var(--md-sys-color-on-primary); + --button-background-hover-opacity: 0.08; + --button-background-focused: var(--md-sys-color-on-primary); + --button-background-focused-opacity: 0.12; + } } } } diff --git a/src/styles/components/ion-alert.scss b/src/styles/components/ion-alert.scss index fa257bd..1e65289 100644 --- a/src/styles/components/ion-alert.scss +++ b/src/styles/components/ion-alert.scss @@ -2,11 +2,15 @@ ion-alert.md:not(.md3-disabled) { --backdrop-opacity: 0.2; --min-width: 280px; --max-width: min(560px, calc(100% - 48px * 2)); - transition: transform 200ms ease; + + transition: transform var(--md-sys-motion-duration-short-3) var(--md-sys-motion-easing-emphasized-decelerate); .alert-wrapper { border-radius: 24px; box-shadow: var(--token-elevation-3); + background: var(--md-sys-color-surface-container-highest); + color: var(--md-sys-color-on-surface); + outline: none; .alert-head { padding-top: 24px; @@ -15,9 +19,42 @@ ion-alert.md:not(.md3-disabled) { .alert-message { padding-bottom: 24px; + &:empty { padding-bottom: 0; } } + + .alert-button { + color: var(--md-sys-color-primary); + + &.alert-button-role-destructive { + color: var(--md-sys-color-error); + } + + &.alert-button-role-preferred { + background: var(--md-sys-color-primary); + color: var(--md-sys-color-on-primary); + border-radius: 999px; + text-transform: none; + + &:hover { + background: color-mix( + in srgb, + var(--md-sys-color-on-primary) var(--md-sys-state-hover-state-layer-opacity), + var(--md-sys-color-primary) + ); + } + + &.ion-activated, + &.ion-focused { + background: color-mix( + in srgb, + var(--md-sys-color-on-primary) var(--md-sys-state-pressed-state-layer-opacity), + var(--md-sys-color-primary) + ); + } + } + } } } diff --git a/src/styles/components/ion-button.scss b/src/styles/components/ion-button.scss index 1cfedfb..a58ec15 100644 --- a/src/styles/components/ion-button.scss +++ b/src/styles/components/ion-button.scss @@ -1,56 +1,81 @@ @mixin theme-button() { - // Default is be round button + // Default is a round/pill button. Match the iOS/Ionic shape. --box-shadow: none; --padding-start: 16px; --padding-end: 16px; --border-radius: 999px; + // Keep both selectors so Ionic's :not(.button-round) rules don't override. + &, &:not(.button-round) { - /** - * for remove default style: - * .sc-ion-buttons-md-s ion-button:not(.button-round) { - * --border-radius: 2px; - * } - */ --border-radius: 999px; } + // Typography uses M3 label-large / sentence case. + font-size: var(--md-sys-typescale-label-large-size); + font-weight: var(--md-sys-typescale-label-large-weight); + letter-spacing: var(--md-sys-typescale-label-large-tracking); + text-transform: none; + + // Default fill is "solid" in Ionic, which maps to M3 "filled". + --background: var(--md-sys-color-primary); + --color: var(--md-sys-color-on-primary); + --background-hover: var(--md-sys-color-on-primary); + --background-hover-opacity: var(--md-sys-state-hover-state-layer-opacity); + --background-focused: var(--md-sys-color-on-primary); + --background-focused-opacity: var(--md-sys-state-focus-state-layer-opacity); + --background-activated: var(--md-sys-color-on-primary); + --background-activated-opacity: var(--md-sys-state-pressed-state-layer-opacity); + &::part(native) { height: 40px; } ion-icon { font-size: 24px; + &[slot='start'] { margin-inline-start: 0; margin-inline-end: 8px; } + &[slot='end'] { margin-inline-start: 8px; margin-inline-end: 0; } } + // Small size (32dp) &.button-small { --padding-start: 12px; --padding-end: 12px; + + font-size: var(--md-sys-typescale-label-medium-size); + font-weight: var(--md-sys-typescale-label-medium-weight); + letter-spacing: var(--md-sys-typescale-label-medium-tracking); + &::part(native) { height: 32px; } + &.button-has-icon-only { width: 32px; height: 32px; } + &.button-has-icon-only::part(native) { width: 32px; height: 32px; } + ion-icon { font-size: 20px; + &[slot='start'] { margin-inline-start: 0; margin-inline-end: 4px; } + &[slot='end'] { margin-inline-start: 4px; margin-inline-end: 0; @@ -58,6 +83,7 @@ } } + // Large size (56dp) &.button-large { --padding-start: 24px; --padding-end: 24px; @@ -65,20 +91,25 @@ &::part(native) { height: 56px; } + &.button-has-icon-only { width: 56px; height: 56px; } + &.button-has-icon-only::part(native) { width: 56px; height: 56px; } + ion-icon { font-size: 24px; + &[slot='start'] { margin-inline-start: 0; margin-inline-end: 8px; } + &[slot='end'] { margin-inline-start: 8px; margin-inline-end: 0; @@ -86,19 +117,60 @@ } } + // Ionic "outline" maps to M3 "outlined". + &.button-outline { + --background: transparent; + --color: var(--md-sys-color-primary); + --border-width: 1px; + --border-style: solid; + --border-color: var(--md-sys-color-outline); + --background-hover: var(--md-sys-color-primary); + --background-focused: var(--md-sys-color-primary); + --background-activated: var(--md-sys-color-primary); + } + + // Ionic "clear" maps to M3 "text". + &.button-clear { + --background: transparent; + --color: var(--md-sys-color-primary); + --border-width: 0; + --background-hover: var(--md-sys-color-primary); + --background-focused: var(--md-sys-color-primary); + --background-activated: var(--md-sys-color-primary); + } + + // Icon-only buttons. Keep the iOS circular 40x40 with a 24dp icon. &.button-has-icon-only { &::part(native) { width: 40px; height: 40px; } + --padding-start: 0; --padding-end: 0; } + // Square shape option. Match the iOS squared-button radius. &.button-square { - --border-radius: 8px; - &.button-square { - --border-radius: 12px; + --border-radius: 12px; + } + + // Respect the Ionic `color` prop on any M3 variant. + &.ion-color { + --background: var(--ion-color-base); + --color: var(--ion-color-contrast); + --border-color: var(--ion-color-base); + --background-hover: var(--ion-color-contrast); + --background-focused: var(--ion-color-contrast); + --background-activated: var(--ion-color-contrast); + + // Outlined / text buttons have a transparent background, so the state + // layer should be the user color itself, not its contrast. + &.button-outline, + &.button-clear { + --background-hover: var(--ion-color-base); + --background-focused: var(--ion-color-base); + --background-activated: var(--ion-color-base); } } } diff --git a/src/styles/components/ion-card.scss b/src/styles/components/ion-card.scss index deae3b0..39199ed 100644 --- a/src/styles/components/ion-card.scss +++ b/src/styles/components/ion-card.scss @@ -1,7 +1,16 @@ ion-card.md:not(.md3-disabled) { + --color: var(--md-sys-color-on-surface); border-radius: 12px; box-shadow: var(--token-elevation-1); + box-sizing: border-box; + // Respect the Ionic `color` prop. + &.ion-color { + --background: var(--ion-color-base); + --color: var(--ion-color-contrast); + } + + // Reduce internal list item padding so content lines up with card insets. ion-list ion-item { --padding-start: 0; } diff --git a/src/styles/components/ion-chip.scss b/src/styles/components/ion-chip.scss index 0548d07..f46bd38 100644 --- a/src/styles/components/ion-chip.scss +++ b/src/styles/components/ion-chip.scss @@ -1,8 +1,33 @@ ion-chip.md:not(.md3-disabled) { + // M3 chips use surface-container-low for the default "assist" style. + --background: var(--md-sys-color-surface-container-low); + --color: var(--md-sys-color-on-surface); + padding-inline-start: 16px; padding-inline-end: 16px; border-radius: 8px; + // A subtle state layer on hover/focus/press. + &:not(.ion-color) { + &:hover { + --background: color-mix(in srgb, var(--md-sys-color-on-surface) 8%, var(--md-sys-color-surface-container-low)); + } + + &:focus { + --background: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, var(--md-sys-color-surface-container-low)); + } + + &.ion-activated { + --background: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, var(--md-sys-color-surface-container-low)); + } + } + + // Respect the Ionic `color` prop. + &.ion-color { + --background: var(--ion-color-base); + --color: var(--ion-color-contrast); + } + ion-icon { font-size: 18px; diff --git a/src/styles/components/ion-content.scss b/src/styles/components/ion-content.scss index f66c4bf..5d3d5d4 100644 --- a/src/styles/components/ion-content.scss +++ b/src/styles/components/ion-content.scss @@ -1,2 +1,4 @@ -.md:not(.md3-disabled) { +ion-content.md:not(.md3-disabled) { + // Use the default Ionic background. Inset lists use color="light" on the + // list itself, not a global content surface override. } diff --git a/src/styles/components/ion-fab.scss b/src/styles/components/ion-fab.scss index aea9262..32e17d6 100644 --- a/src/styles/components/ion-fab.scss +++ b/src/styles/components/ion-fab.scss @@ -1,10 +1,21 @@ ion-fab.md:not(.md3-disabled) { ion-fab-button { - width: 56px; - height: 56px; + --background: var(--md-sys-color-primary-container); + --color: var(--md-sys-color-on-primary-container); --border-radius: 16px; - --transition: border-radius var(--token-expressive-fast-special); --box-shadow: var(--token-elevation-3); + --transition: border-radius var(--md-sys-motion-duration-medium-3) var(--md-sys-motion-easing-emphasized-decelerate); + + // State layers use the on-container color. + --background-hover: var(--md-sys-color-on-primary-container); + --background-hover-opacity: var(--md-sys-state-hover-state-layer-opacity); + --background-focused: var(--md-sys-color-on-primary-container); + --background-focused-opacity: var(--md-sys-state-focus-state-layer-opacity); + --background-activated: var(--md-sys-color-on-primary-container); + --background-activated-opacity: var(--md-sys-state-pressed-state-layer-opacity); + + width: 56px; + height: 56px; ion-icon { width: 24px; @@ -23,10 +34,20 @@ ion-fab.md:not(.md3-disabled) { width: 40px; height: 40px; --border-radius: 12px; + &.fab-button-close-active { --border-radius: 50%; } } + + // Respect the Ionic `color` prop. + &.ion-color { + --background: var(--ion-color-base); + --color: var(--ion-color-contrast); + --background-hover: var(--ion-color-contrast); + --background-focused: var(--ion-color-contrast); + --background-activated: var(--ion-color-contrast); + } } &:has(.fab-button-small) { diff --git a/src/styles/components/ion-list.scss b/src/styles/components/ion-list.scss index 5319393..b793d98 100644 --- a/src/styles/components/ion-list.scss +++ b/src/styles/components/ion-list.scss @@ -15,7 +15,9 @@ ion-list.md:not(.md3-disabled).list-inset { @include structured-list.layout(8px, $group-border-radius); :is(#{structured-list.$groups}) ion-item { - --border-color: var(--ion-color-light, #fff); + // Match the Android inset-list behaviour: the divider is the same colour + // as the item background, so it does not separate items visually. + --border-color: var(--ion-item-background, var(--ion-background-color, #fff)); ion-note[slot='end'] { font-size: max(14px, 1rem); @@ -40,7 +42,7 @@ ion-list.md:not(.md3-disabled).list-inset { } & > ion-input[labelplacement='floating'] { - transition: transform 200ms ease; + transition: transform var(--md-sys-motion-duration-short-2) var(--md-sys-motion-easing-standard); &:not(.label-floating) { transform: translateY(-7px); diff --git a/src/styles/components/ion-loading.scss b/src/styles/components/ion-loading.scss index aa7c3e4..dda8eb7 100644 --- a/src/styles/components/ion-loading.scss +++ b/src/styles/components/ion-loading.scss @@ -2,5 +2,7 @@ ion-loading.md:not(.md3-disabled) { .loading-wrapper { border-radius: 24px; box-shadow: var(--token-elevation-3); + background: var(--md-sys-color-surface-container-highest); + color: var(--md-sys-color-on-surface); } } diff --git a/src/styles/components/ion-modal.scss b/src/styles/components/ion-modal.scss index a875fc5..4655415 100644 --- a/src/styles/components/ion-modal.scss +++ b/src/styles/components/ion-modal.scss @@ -1,13 +1,17 @@ ion-modal.md:not(.md3-disabled) { --box-shadow: var(--token-elevation-3); + @media only screen and (min-width: 768px) and (min-height: 600px) { --border-radius: 24px; } + ion-toolbar { --min-height: 64px; + &:first-of-type { padding-top: calc(var(--ion-safe-area-top, 0) + 6px); } + padding-right: var(--ion-safe-area-right); padding-left: var(--ion-safe-area-left); @@ -16,11 +20,13 @@ ion-modal.md:not(.md3-disabled) { width: 48px; height: 48px; } + ion-icon[name='close-outline'] { font-size: 2rem; } } } + &::part(handle) { width: 80px; } diff --git a/src/styles/components/ion-popover.scss b/src/styles/components/ion-popover.scss index ddacca3..b71f41a 100644 --- a/src/styles/components/ion-popover.scss +++ b/src/styles/components/ion-popover.scss @@ -1,5 +1,8 @@ ion-popover.md:not(.md3-disabled) { --box-shadow: var(--token-elevation-3); + --background: var(--md-sys-color-surface-container); + --color: var(--md-sys-color-on-surface); + &::part(content) { border-radius: 12px; } diff --git a/src/styles/components/ion-range.scss b/src/styles/components/ion-range.scss index 1110708..0e2196c 100644 --- a/src/styles/components/ion-range.scss +++ b/src/styles/components/ion-range.scss @@ -1,143 +1,20 @@ ion-range.md:not(.md3-disabled) { - --height: 56px; - --bar-height: 16px; - --bar-border-radius: 8px; + // Use Ionic's default slider geometry (height, bar-height, knob size, + // border-radius, etc.) and only layer MD3 color tokens on top. + --bar-background: var(--md-sys-color-primary-container); + --bar-background-active: var(--md-sys-color-primary); - --knob-background: var(--ion-color-primary); + --knob-background: var(--md-sys-color-on-primary); --knob-box-shadow: none; - --knob-border-radius: 8px; - --knob-size: 44px; - --pin-background: var(--ion-background-color-step-700, #4a4458); - - &.range-has-pin { - padding-top: 2.6rem; - } - - &::part(knob) { - width: 4px; - transition: - width var(--token-expressive-fast-effects), - transform var(--token-expressive-fast-effects), - outline var(--token-expressive-fast-effects); - will-change: width; - transform: translateX(calc(var(--knob-size) / 2 - 2px)); - &:before { - content: none; - } - } - - &:dir(rtl)::part(knob) { - transform: translateX(calc((var(--knob-size) / 2 - 2px) * -1)); - } + --pin-background: var(--md-sys-color-inverse-surface); + --pin-color: var(--md-sys-color-inverse-on-surface); + // Preserve the Ionic `color` prop behaviour. &.ion-color { - --knob-background: var(--ion-color-base, var(--ion-color-primary)); - } - - &::part(tick), - &::part(tick-active) { - --bar-height: 4px; - } - - &:not(.range-dual-knobs) { - &.range-pressed::part(knob) { - width: 2px; - transform: translateX(calc(var(--knob-size) / 2 - 1px)); - } - - &.range-pressed:dir(rtl)::part(knob) { - transform: translateX(calc((var(--knob-size) / 2 - 1px) * -1)); - } - - &:not(.range-value-min):not(.range-value-max)::part(knob) { - outline: 6px solid var(--ion-background-color, #ffffff); - } - &.range-pressed:not(.range-value-min):not(.range-value-max)::part(knob) { - outline: 7px solid var(--ion-background-color, #ffffff); - } - - &:not(.range-value-max)::part(bar-active) { - border-start-end-radius: calc(var(--knob-border-radius) / 2); - border-end-end-radius: calc(var(--knob-border-radius) / 2); - margin-inline-end: 6px; - } - - &.range-value-min { - &::part(bar) { - border-start-start-radius: calc(var(--knob-border-radius) / 2); - border-end-start-radius: calc(var(--knob-border-radius) / 2); - width: calc(100% - 6px); - inset-inline-end: 0; - } - } - - &.range-value-max { - &::part(bar), - &::part(bar-active) { - border-start-end-radius: calc(var(--knob-border-radius) / 2); - border-end-end-radius: calc(var(--knob-border-radius) / 2); - width: calc(100% - 6px); - inset-inline-start: 0; - } - } - } - - &.range-dual-knobs { - &.range-pressed-a::part(knob-a), - &.range-pressed-b::part(knob-b) { - width: 2px; - transform: translateX(calc(var(--knob-size) / 2 - 1px)); - } - - &.range-pressed-a:dir(rtl)::part(knob-a), - &.range-pressed-b:dir(rtl)::part(knob-b) { - transform: translateX(calc((var(--knob-size) / 2 - 1px) * -1)); - } - - &.range-value-min { - &::part(bar), - &::part(bar-active) { - border-start-start-radius: calc(var(--knob-border-radius) / 2); - border-end-start-radius: calc(var(--knob-border-radius) / 2); - margin-inline-start: 6px; - } - &::part(bar) { - width: calc(100% - 6px); - } - } - &.range-value-max { - &::part(bar), - &::part(bar-active) { - border-start-end-radius: calc(var(--knob-border-radius) / 2); - border-end-end-radius: calc(var(--knob-border-radius) / 2); - margin-inline-end: 6px; - } - &::part(bar) { - width: calc(100% - 6px); - } - } - &.range-value-min.range-value-max::part(bar) { - width: calc(100% - 12px); - } - - &:not(.range-value-min)::part(knob-lower), - &:not(.range-value-max)::part(knob-upper) { - outline: 6px solid var(--ion-background-color, #ffffff); - } - - &.range-pressed:not(.range-value-min)::part(knob-lower), - &.range-pressed:not(.range-value-max)::part(knob-upper) { - outline: 7px solid var(--ion-background-color, #ffffff); - } - } - - &::part(pin) { - padding: 16px; - min-width: 36px; - top: 10px; - &:before { - content: none; - } + --bar-background-active: var(--ion-color-base, var(--ion-color-primary)); + --knob-background: var(--ion-color-contrast, var(--ion-color-primary-contrast)); + --pin-background: var(--ion-color-base, var(--ion-color-primary)); + --pin-color: var(--ion-color-contrast, var(--ion-color-primary-contrast)); } } diff --git a/src/styles/components/ion-searchbar.scss b/src/styles/components/ion-searchbar.scss index 791a402..66626fc 100644 --- a/src/styles/components/ion-searchbar.scss +++ b/src/styles/components/ion-searchbar.scss @@ -1,7 +1,10 @@ ion-searchbar.md:not(.md3-disabled) { --box-shadow: none; --border-radius: 999px; - --background: rgba(var(--ion-color-contrast-rgb, var(--ion-text-color-rgb, 0, 0, 0)), 0.08); - --color: rgb(var(--ion-color-contrast-rgb, var(--ion-text-color-rgb, 0, 0, 0))); - --icon-color: rgb(var(--ion-color-contrast-rgb, var(--ion-text-color-rgb, 0, 0, 0))); + --background: color-mix(in srgb, var(--md-sys-color-on-surface) 8%, transparent); + --color: var(--md-sys-color-on-surface); + --icon-color: var(--md-sys-color-on-surface-variant); + --placeholder-color: var(--md-sys-color-on-surface-variant); + --clear-button-color: var(--md-sys-color-on-surface-variant); + --cancel-button-color: var(--md-sys-color-on-surface-variant); } diff --git a/src/styles/components/ion-segment.scss b/src/styles/components/ion-segment.scss index a8f53d2..b7a92d9 100644 --- a/src/styles/components/ion-segment.scss +++ b/src/styles/components/ion-segment.scss @@ -1,7 +1,3 @@ ion-segment.md:not(.md3-disabled) { - border-bottom: 1px solid - var( - --ion-item-border-color, - var(--ion-border-color, var(--ion-color-step-150, var(--ion-background-color-step-150, rgba(0, 0, 0, 0.13)))) - ); + border-bottom: 1px solid var(--md-sys-color-outline); } diff --git a/src/styles/components/ion-tabs.scss b/src/styles/components/ion-tabs.scss index 8c06f45..be76671 100644 --- a/src/styles/components/ion-tabs.scss +++ b/src/styles/components/ion-tabs.scss @@ -1,14 +1,20 @@ ion-tab-bar.md:not(.md3-disabled) { + --background: var(--md-sys-color-surface-container); height: 62px; + ion-tab-button { --ripple-color: transparent; - --color-selected: var(--ion-color-contrast, var(--ion-color-primary-shade)); + --color: var(--md-sys-color-on-surface-variant); + --color-selected: var(--md-sys-color-on-surface); + ion-icon { padding: 4px 16px; margin-top: 6px; border-radius: 16px; - width: calc(56px - 32px); - height: calc(32px - 8px); + width: 24px; + height: 24px; + color: var(--md-sys-color-on-surface-variant); + &::before { content: ''; position: absolute; @@ -16,19 +22,26 @@ ion-tab-bar.md:not(.md3-disabled) { top: 0; width: 100%; height: 100%; - background-color: rgba(var(--ion-color-contrast-rgb, var(--ion-color-primary-rgb, 2, 137, 255)), 0.2); + background-color: var(--md-sys-color-secondary-container); transform: scaleX(0); - transition: transform var(--token-expressive-fast-effects); + transition: transform var(--md-sys-motion-duration-short-3) var(--md-sys-motion-easing-emphasized); z-index: -1; } } + ion-label { margin-top: 2px; margin-bottom: 6px; + font-size: var(--md-sys-typescale-label-medium-size); } + &.tab-selected { - ion-icon::before { - transform: scaleX(1); + ion-icon { + color: var(--md-sys-color-on-secondary-container); + + &::before { + transform: scaleX(1); + } } } } diff --git a/src/styles/components/ion-toast.scss b/src/styles/components/ion-toast.scss index 6affd7d..7717106 100644 --- a/src/styles/components/ion-toast.scss +++ b/src/styles/components/ion-toast.scss @@ -1,4 +1,7 @@ ion-toast.md:not(.md3-disabled) { + --background: var(--md-sys-color-inverse-surface); + --color: var(--md-sys-color-inverse-on-surface); + --button-color: var(--md-sys-color-inverse-primary); --box-shadow: var(--token-elevation-3); --border-radius: 8px; } diff --git a/src/styles/components/ion-toggle.scss b/src/styles/components/ion-toggle.scss index 927da04..6d449bf 100644 --- a/src/styles/components/ion-toggle.scss +++ b/src/styles/components/ion-toggle.scss @@ -5,33 +5,42 @@ ion-toggle.md:not(.md3-disabled) { --border-radius: 16px; --handle-box-shadow: none; --handle-spacing: 2px; - --handle-transition: transform var(--token-expressive-default-special), background-color var(--token-expressive-default-effects); - --track-background: var(--ion-background-color-step-100, #e6e0e9); - --handle-background: var(--ion-text-color-step-450, #79747e); + --handle-transition: + transform var(--md-sys-motion-duration-short-3) var(--md-sys-motion-easing-standard), + background-color var(--md-sys-motion-duration-short-3) var(--md-sys-motion-easing-standard); + + // M3 switch track: surface-container-highest when off, primary when on. + --track-background: var(--md-sys-color-surface-container-highest); + --track-background-checked: var(--md-sys-color-primary); + + // M3 switch handle: outline when off, on-primary when on. + --handle-background: var(--md-sys-color-outline); + --handle-background-checked: var(--md-sys-color-on-primary); &::part(track) { box-sizing: border-box; min-height: 32px; min-width: 52px; - border: 2px solid var(--ion-text-color-step-450, #79747e); + border: 2px solid var(--md-sys-color-outline); } &.toggle-checked { - --track-background-checked: var(--ion-color-base); - --handle-background-checked: var(--ion-color-contrast); + --track-background-checked: var(--md-sys-color-primary); + --handle-background-checked: var(--md-sys-color-on-primary); &::part(track) { - border-color: var(--ion-color-base); + border-color: var(--md-sys-color-primary); } } - &:not(.ion-color) { + // Respect the Ionic `color` prop. + &.ion-color { &.toggle-checked { - --track-background-checked: var(--ion-color-primary); - --handle-background-checked: var(--ion-color-primary-contrast); + --track-background-checked: var(--ion-color-base); + --handle-background-checked: var(--ion-color-contrast); &::part(track) { - border-color: var(--ion-color-primary); + border-color: var(--ion-color-base); } } } diff --git a/src/styles/components/ion-toolbar.scss b/src/styles/components/ion-toolbar.scss index 252df0a..9d9d040 100644 --- a/src/styles/components/ion-toolbar.scss +++ b/src/styles/components/ion-toolbar.scss @@ -4,14 +4,24 @@ ion-footer.md:not(.md3-disabled) { } ion-toolbar.md:not(.md3-disabled) { + // Respect the Ionic `color` prop. + &.ion-color { + --background: var(--ion-color-base); + --color: var(--ion-color-contrast); + --ion-toolbar-color: var(--ion-color-contrast); + --ion-toolbar-background: var(--ion-color-base); + } + ion-buttons[slot='start'], ion-back-button[slot='start'] { margin-inline-start: 16px; } + ion-buttons[slot='end'], ion-back-button[slot='end'] { margin-inline-end: 16px; } + /* fallback ion-buttons[slot=end], ion-back-button[slot=end] */ ion-buttons > ion-back-button { margin-inline: 0; @@ -20,6 +30,7 @@ ion-toolbar.md:not(.md3-disabled) { &:has(> [slot='start']) ion-title { padding-inline-start: 4px; } + &:not(:has(> [slot='start'])) ion-title { padding-inline-start: calc(16px + 12px); } @@ -27,6 +38,7 @@ ion-toolbar.md:not(.md3-disabled) { &.toolbar-searchbar:not(:has(> [slot='start'])) { --padding-start: 10px; } + &.toolbar-searchbar:not(:has(> [slot='end'])) { --padding-end: 10px; } @@ -46,6 +58,7 @@ ion-toolbar.md:not(.md3-disabled) { width: 48px; height: 48px; } + &.button-solid { &::part(native) { width: 40px; diff --git a/src/styles/default-variables.scss b/src/styles/default-variables.scss index e25007f..51a69c1 100644 --- a/src/styles/default-variables.scss +++ b/src/styles/default-variables.scss @@ -1,20 +1,241 @@ /** - * @naming - * prefix: ios26 - * target-element: tab-button - * target css property: color - * status: selected - * additional information: rgb + * @license MIT + * + * Material Design 3 system tokens derived from the host Ionic palette. + * + * These tokens are exposed as CSS custom properties so the theme and consumers + * can reference them. The architecture has three layers: + * + * 1. Source colors (--md-sys-source-*) + * The "seed" colors Material Color Utilities would use to generate a tonal + * palette. They map to Ionic variables by default and are fully overridable + * for Material You / dynamic color. + * + * 2. MD3 color roles (--md-sys-color-*) + * The public API components consume. When no override is provided, the + * source colors are transformed through a CSS-only fallback generator + * (color-mix). This is intentionally an adapter, not the canonical M3 + * HCT algorithm. + * + * 3. Components + * Reference only --md-sys-color-* and never --ion-color-* directly. + * + * This boundary lets the theme keep working with plain Ionic palettes today + * while allowing a future color-scheme generator to replace layer 2 without + * touching component styles. */ :root { - --token-elevation-0: rgba(0, 0, 0, 0.2) 0px 0px 0px 0px, rgba(0, 0, 0, 0.14) 0px 0px 0px 0px, rgba(0, 0, 0, 0.12) 0px 0px 0px 0px; - --token-elevation-1: rgba(0, 0, 0, 0.2) 0px 2px 1px -1px, rgba(0, 0, 0, 0.14) 0px 1px 1px 0px, rgba(0, 0, 0, 0.12) 0px 1px 3px 0px; - --token-elevation-3: rgba(0, 0, 0, 0.2) 0px 3px 3px -2px, rgba(0, 0, 0, 0.14) 0px 3px 4px 0px, rgba(0, 0, 0, 0.12) 0px 1px 8px 0px; - --token-elevation-6: rgba(0, 0, 0, 0.2) 0px 3px 5px -1px, rgba(0, 0, 0, 0.14) 0px 6px 10px 0px, rgba(0, 0, 0, 0.12) 0px 1px 18px 0px; - --token-elevation-8: rgba(0, 0, 0, 0.2) 0px 5px 5px -3px, rgba(0, 0, 0, 0.14) 0px 8px 10px 1px, rgba(0, 0, 0, 0.12) 0px 3px 14px 2px; - --token-elevation-12: rgba(0, 0, 0, 0.2) 0px 7px 8px -4px, rgba(0, 0, 0, 0.14) 0px 12px 17px 2px, rgba(0, 0, 0, 0.12) 0px 5px 22px 4px; + // --------------------------------------------------------------------------- + // Source colors + // --------------------------------------------------------------------------- + + --md-sys-source-primary: var(--ion-color-primary, #6750a4); + --md-sys-source-secondary: var(--ion-color-secondary, #625b71); + --md-sys-source-tertiary: var(--ion-color-tertiary, #7d5260); + --md-sys-source-error: var(--ion-color-danger, #b3261e); + + // Neutral sources: a desaturated version of the primary hue plus the app's + // neutral gray. Overriding --md-sys-source-neutral / -variant is the easiest + // way to feed a custom Material You neutral palette. + --md-sys-source-neutral: color-mix(in srgb, var(--md-sys-source-primary) 15%, var(--ion-color-medium, #79747e)); + --md-sys-source-neutral-variant: color-mix(in srgb, var(--md-sys-source-primary) 25%, var(--ion-color-medium, #79747e)); + + --md-sys-source-background: var(--ion-background-color, #ffffff); + --md-sys-source-text: var(--ion-text-color, #000000); + + // --------------------------------------------------------------------------- + // Color roles + // --------------------------------------------------------------------------- + + // Primary + --md-sys-color-primary: var(--md-sys-source-primary); + --md-sys-color-on-primary: var(--ion-color-primary-contrast, #ffffff); + --md-sys-color-primary-container: color-mix(in srgb, var(--md-sys-source-primary) 18%, var(--md-sys-source-background)); + --md-sys-color-on-primary-container: color-mix(in srgb, var(--md-sys-source-primary) 20%, var(--md-sys-source-text)); + + // Secondary + --md-sys-color-secondary: var(--md-sys-source-secondary); + --md-sys-color-on-secondary: var(--ion-color-secondary-contrast, #ffffff); + --md-sys-color-secondary-container: color-mix(in srgb, var(--md-sys-source-secondary) 18%, var(--md-sys-source-background)); + --md-sys-color-on-secondary-container: color-mix(in srgb, var(--md-sys-source-secondary) 20%, var(--md-sys-source-text)); + + // Tertiary + --md-sys-color-tertiary: var(--md-sys-source-tertiary); + --md-sys-color-on-tertiary: var(--ion-color-tertiary-contrast, #ffffff); + --md-sys-color-tertiary-container: color-mix(in srgb, var(--md-sys-source-tertiary) 18%, var(--md-sys-source-background)); + --md-sys-color-on-tertiary-container: color-mix(in srgb, var(--md-sys-source-tertiary) 20%, var(--md-sys-source-text)); + + // Error + --md-sys-color-error: var(--md-sys-source-error); + --md-sys-color-on-error: var(--ion-color-danger-contrast, #ffffff); + --md-sys-color-error-container: color-mix(in srgb, var(--md-sys-source-error) 18%, var(--md-sys-source-background)); + --md-sys-color-on-error-container: color-mix(in srgb, var(--md-sys-source-error) 20%, var(--md-sys-source-text)); + + // Surfaces + --md-sys-color-background: var(--md-sys-source-background); + --md-sys-color-surface: var(--md-sys-source-background); + --md-sys-color-surface-tint: var(--md-sys-source-primary); + --md-sys-color-surface-dim: color-mix(in srgb, var(--md-sys-source-neutral) 10%, var(--md-sys-source-background)); + --md-sys-color-surface-bright: color-mix(in srgb, var(--md-sys-source-neutral) 2%, var(--md-sys-source-background)); + --md-sys-color-surface-variant: color-mix(in srgb, var(--md-sys-source-neutral) 10%, var(--md-sys-source-background)); + --md-sys-color-surface-container-lowest: var(--md-sys-source-background); + --md-sys-color-surface-container-low: color-mix(in srgb, var(--md-sys-source-neutral) 2%, var(--md-sys-source-background)); + --md-sys-color-surface-container: color-mix(in srgb, var(--md-sys-source-neutral) 4%, var(--md-sys-source-background)); + --md-sys-color-surface-container-high: color-mix(in srgb, var(--md-sys-source-neutral) 6%, var(--md-sys-source-background)); + --md-sys-color-surface-container-highest: color-mix(in srgb, var(--md-sys-source-neutral) 8%, var(--md-sys-source-background)); + + // Content colors + --md-sys-color-on-surface: var(--md-sys-source-text); + --md-sys-color-on-surface-variant: color-mix(in srgb, var(--md-sys-source-neutral-variant) 60%, var(--md-sys-source-text)); + --md-sys-color-outline: color-mix(in srgb, var(--md-sys-source-neutral-variant) 50%, var(--md-sys-source-text)); + --md-sys-color-outline-variant: color-mix(in srgb, var(--md-sys-source-neutral) 40%, var(--md-sys-source-background)); + + // Inverse + --md-sys-color-inverse-surface: var(--ion-color-dark, #322f35); + --md-sys-color-inverse-on-surface: var(--ion-color-dark-contrast, #f5eff7); + --md-sys-color-inverse-primary: var(--ion-color-primary-tint, #d0bcff); + + // Misc + --md-sys-color-scrim: rgba(0, 0, 0, 0.32); + --md-sys-color-shadow: rgba(0, 0, 0, 0.2); + + // --------------------------------------------------------------------------- + // Shape + // --------------------------------------------------------------------------- + + --md-sys-shape-corner-none: 0; + --md-sys-shape-corner-extra-small: 4px; + --md-sys-shape-corner-small: 8px; + --md-sys-shape-corner-medium: 12px; + --md-sys-shape-corner-large: 16px; + --md-sys-shape-corner-extra-large: 28px; + --md-sys-shape-corner-full: 999px; + + // --------------------------------------------------------------------------- + // Typography + // --------------------------------------------------------------------------- + + // M3's default typeface is Roboto, but we inherit the app's font family. + --md-sys-typescale-font: var( + --ion-font-family, + system-ui, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + 'Helvetica Neue', + Arial, + sans-serif + ); + + --md-sys-typescale-display-large-size: 3.5625rem; + --md-sys-typescale-display-large-line-height: 4rem; + --md-sys-typescale-display-large-weight: 400; + --md-sys-typescale-display-large-tracking: -0.015625rem; + + --md-sys-typescale-display-medium-size: 2.8125rem; + --md-sys-typescale-display-medium-line-height: 3.25rem; + --md-sys-typescale-display-medium-weight: 400; + --md-sys-typescale-display-medium-tracking: 0; + + --md-sys-typescale-display-small-size: 2.25rem; + --md-sys-typescale-display-small-line-height: 2.75rem; + --md-sys-typescale-display-small-weight: 400; + --md-sys-typescale-display-small-tracking: 0; + + --md-sys-typescale-headline-large-size: 2rem; + --md-sys-typescale-headline-large-line-height: 2.5rem; + --md-sys-typescale-headline-large-weight: 400; + --md-sys-typescale-headline-large-tracking: 0; + + --md-sys-typescale-headline-medium-size: 1.75rem; + --md-sys-typescale-headline-medium-line-height: 2.25rem; + --md-sys-typescale-headline-medium-weight: 400; + --md-sys-typescale-headline-medium-tracking: 0; + + --md-sys-typescale-headline-small-size: 1.5rem; + --md-sys-typescale-headline-small-line-height: 2rem; + --md-sys-typescale-headline-small-weight: 400; + --md-sys-typescale-headline-small-tracking: 0; + --md-sys-typescale-title-large-size: 1.375rem; + --md-sys-typescale-title-large-line-height: 1.75rem; + --md-sys-typescale-title-large-weight: 400; + --md-sys-typescale-title-large-tracking: 0; + + --md-sys-typescale-title-medium-size: 1rem; + --md-sys-typescale-title-medium-line-height: 1.5rem; + --md-sys-typescale-title-medium-weight: 500; + --md-sys-typescale-title-medium-tracking: 0.009375rem; + + --md-sys-typescale-title-small-size: 0.875rem; + --md-sys-typescale-title-small-line-height: 1.25rem; + --md-sys-typescale-title-small-weight: 500; + --md-sys-typescale-title-small-tracking: 0.00625rem; + + --md-sys-typescale-body-large-size: 1rem; + --md-sys-typescale-body-large-line-height: 1.5rem; + --md-sys-typescale-body-large-weight: 400; + --md-sys-typescale-body-large-tracking: 0.03125rem; + + --md-sys-typescale-body-medium-size: 0.875rem; + --md-sys-typescale-body-medium-line-height: 1.25rem; + --md-sys-typescale-body-medium-weight: 400; + --md-sys-typescale-body-medium-tracking: 0.015625rem; + + --md-sys-typescale-body-small-size: 0.75rem; + --md-sys-typescale-body-small-line-height: 1rem; + --md-sys-typescale-body-small-weight: 400; + --md-sys-typescale-body-small-tracking: 0.025rem; + + --md-sys-typescale-label-large-size: 0.875rem; + --md-sys-typescale-label-large-line-height: 1.25rem; + --md-sys-typescale-label-large-weight: 500; + --md-sys-typescale-label-large-tracking: 0.00625rem; + + --md-sys-typescale-label-medium-size: 0.75rem; + --md-sys-typescale-label-medium-line-height: 1rem; + --md-sys-typescale-label-medium-weight: 500; + --md-sys-typescale-label-medium-tracking: 0.03125rem; + + --md-sys-typescale-label-small-size: 0.6875rem; + --md-sys-typescale-label-small-line-height: 1rem; + --md-sys-typescale-label-small-weight: 500; + --md-sys-typescale-label-small-tracking: 0.03125rem; + + // --------------------------------------------------------------------------- + // Motion + // --------------------------------------------------------------------------- + + // M3 easing curves + --md-sys-motion-easing-standard: cubic-bezier(0.2, 0, 0, 1); + --md-sys-motion-easing-standard-decelerate: cubic-bezier(0, 0, 0, 1); + --md-sys-motion-easing-standard-accelerate: cubic-bezier(0.3, 0, 1, 1); + --md-sys-motion-easing-emphasized: cubic-bezier(0.2, 0, 0, 1); + --md-sys-motion-easing-emphasized-decelerate: cubic-bezier(0.05, 0.7, 0.1, 1); + --md-sys-motion-easing-emphasized-accelerate: cubic-bezier(0.3, 0, 0.8, 0.15); + --md-sys-motion-easing-linear: cubic-bezier(0, 0, 1, 1); + + // M3 duration tokens + --md-sys-motion-duration-short-1: 50ms; + --md-sys-motion-duration-short-2: 100ms; + --md-sys-motion-duration-short-3: 150ms; + --md-sys-motion-duration-short-4: 200ms; + --md-sys-motion-duration-medium-1: 250ms; + --md-sys-motion-duration-medium-2: 300ms; + --md-sys-motion-duration-medium-3: 350ms; + --md-sys-motion-duration-medium-4: 400ms; + --md-sys-motion-duration-long-1: 450ms; + --md-sys-motion-duration-long-2: 500ms; + --md-sys-motion-duration-long-3: 550ms; + --md-sys-motion-duration-long-4: 600ms; + --md-sys-motion-duration-extra-long-1: 700ms; + --md-sys-motion-duration-extra-long-2: 800ms; + --md-sys-motion-duration-extra-long-3: 900ms; + --md-sys-motion-duration-extra-long-4: 1000ms; + + // Legacy expressive motion tokens (kept for backwards compatibility) --token-expressive-fast-special: 350ms cubic-bezier(0.42, 1.67, 0.21, 0.9); --token-expressive-default-special: 500ms cubic-bezier(0.38, 1.21, 0.22, 1); --token-expressive-slow-special: 650ms cubic-bezier(0.39, 1.29, 0.35, 0.98); @@ -22,4 +243,69 @@ --token-expressive-fast-effects: 150ms cubic-bezier(0.31, 0.94, 0.34, 1); --token-expressive-default-effects: 200ms cubic-bezier(0.34, 0.8, 0.34, 1); --token-expressive-slow-effects: 300ms cubic-bezier(0.34, 0.88, 0.34, 1); + + // --------------------------------------------------------------------------- + // State + // --------------------------------------------------------------------------- + + --md-sys-state-hover-state-layer-opacity: 0.08; + --md-sys-state-focus-state-layer-opacity: 0.12; + --md-sys-state-pressed-state-layer-opacity: 0.12; + --md-sys-state-drag-state-layer-opacity: 0.16; + --md-sys-state-disabled-container-opacity: 0.12; + --md-sys-state-disabled-content-opacity: 0.38; + + // --------------------------------------------------------------------------- + // Elevation + // --------------------------------------------------------------------------- + + --md-sys-elevation-level-0: 0 0 0 0 rgba(0, 0, 0, 0.2), 0 0 0 0 rgba(0, 0, 0, 0.14), 0 0 0 0 rgba(0, 0, 0, 0.12); + --md-sys-elevation-level-1: rgba(0, 0, 0, 0.2) 0px 2px 1px -1px, rgba(0, 0, 0, 0.14) 0px 1px 1px 0px, rgba(0, 0, 0, 0.12) 0px 1px 3px 0px; + --md-sys-elevation-level-2: rgba(0, 0, 0, 0.2) 0px 3px 1px -2px, rgba(0, 0, 0, 0.14) 0px 2px 2px 0px, rgba(0, 0, 0, 0.12) 0px 1px 5px 0px; + --md-sys-elevation-level-3: rgba(0, 0, 0, 0.2) 0px 3px 3px -2px, rgba(0, 0, 0, 0.14) 0px 3px 4px 0px, rgba(0, 0, 0, 0.12) 0px 1px 8px 0px; + --md-sys-elevation-level-4: + rgba(0, 0, 0, 0.2) 0px 2px 4px -1px, rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px; + --md-sys-elevation-level-5: + rgba(0, 0, 0, 0.2) 0px 3px 5px -1px, rgba(0, 0, 0, 0.14) 0px 6px 10px 0px, rgba(0, 0, 0, 0.12) 0px 1px 18px 0px; + + // Legacy token aliases (kept for backwards compatibility) + // md-sys-elevation-level-0..5 are new M3 levels. The old 0/1/3 align to them, + // and old 6 aligns to M3 level 5. Old 8/12 have no M3 equivalent, so their + // original values are kept verbatim. + --token-elevation-0: var(--md-sys-elevation-level-0); + --token-elevation-1: var(--md-sys-elevation-level-1); + --token-elevation-3: var(--md-sys-elevation-level-3); + --token-elevation-6: var(--md-sys-elevation-level-5); + --token-elevation-8: rgba(0, 0, 0, 0.2) 0px 5px 5px -3px, rgba(0, 0, 0, 0.14) 0px 8px 10px 1px, rgba(0, 0, 0, 0.12) 0px 3px 14px 2px; + --token-elevation-12: rgba(0, 0, 0, 0.2) 0px 7px 8px -4px, rgba(0, 0, 0, 0.14) 0px 12px 17px 2px, rgba(0, 0, 0, 0.12) 0px 5px 22px 4px; +} + +// --------------------------------------------------------------------------- +// Dark palette overrides +// --------------------------------------------------------------------------- +// When Ionic applies its dark palette, the background/text foundation +// switches to dark mode. These overrides adapt the derived Material roles +// for dark surfaces while preserving Ionic colors as the theme source. + +html.ion-palette-dark { + // Container tones: use more source against a black background. + --md-sys-color-primary-container: color-mix(in srgb, var(--md-sys-source-primary) 35%, var(--md-sys-source-background)); + --md-sys-color-secondary-container: color-mix(in srgb, var(--md-sys-source-secondary) 35%, var(--md-sys-source-background)); + --md-sys-color-tertiary-container: color-mix(in srgb, var(--md-sys-source-tertiary) 35%, var(--md-sys-source-background)); + --md-sys-color-error-container: color-mix(in srgb, var(--md-sys-source-error) 35%, var(--md-sys-source-background)); + + // Surfaces: bring surface containers up to M3 dark tone range. + --md-sys-color-surface-dim: color-mix(in srgb, var(--md-sys-source-neutral) 10%, var(--md-sys-source-background)); + --md-sys-color-surface-bright: color-mix(in srgb, var(--md-sys-source-neutral) 24%, var(--md-sys-source-background)); + --md-sys-color-surface-variant: color-mix(in srgb, var(--md-sys-source-neutral) 10%, var(--md-sys-source-background)); + --md-sys-color-surface-container-lowest: var(--md-sys-source-background); + --md-sys-color-surface-container-low: color-mix(in srgb, var(--md-sys-source-neutral) 8%, var(--md-sys-source-background)); + --md-sys-color-surface-container: color-mix(in srgb, var(--md-sys-source-neutral) 12%, var(--md-sys-source-background)); + --md-sys-color-surface-container-high: color-mix(in srgb, var(--md-sys-source-neutral) 16%, var(--md-sys-source-background)); + --md-sys-color-surface-container-highest: color-mix(in srgb, var(--md-sys-source-neutral) 20%, var(--md-sys-source-background)); + + // Inverse flips from dark to light in a dark palette. + --md-sys-color-inverse-surface: var(--ion-color-dark, #e6e0e9); + --md-sys-color-inverse-on-surface: var(--ion-color-dark-contrast, #1d1b20); + --md-sys-color-inverse-primary: var(--ion-color-primary-tint, #d0bcff); }