From 3e8a88812a53fe166df81bb81fc07a45570d8568 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:36:09 -0400 Subject: [PATCH 1/3] feat(css): add new display css classes and test --- core/src/css/display.scss | 35 +++++++++++++++++++++-- core/src/css/test/display.e2e.ts | 48 ++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 core/src/css/test/display.e2e.ts diff --git a/core/src/css/display.scss b/core/src/css/display.scss index 30b1db4095b..c2d808fc534 100644 --- a/core/src/css/display.scss +++ b/core/src/css/display.scss @@ -2,9 +2,14 @@ @import "../themes/ionic.mixins"; // Display -// -------------------------------------------------- -// Modifies display of a particular element based on the given classes +// ------------------------------------------------------------------ +// Provides utility classes to control the CSS display property +// of elements. Includes responsive variants for toggling between +// block, inline, flex, grid, and other display values at different +// breakpoints. +// TODO(FW-6697): remove ion-hide-* classes in favor of the new +// ion-display-* classes .ion-hide { display: none !important; } @@ -29,3 +34,29 @@ } } } + +$display-values: ( + none, + inline, + inline-block, + block, + flex, + inline-flex, + grid, + inline-grid, + table, + table-cell, + table-row +); + +@each $display in $display-values { + @each $breakpoint in map-keys($screen-breakpoints) { + $infix: breakpoint-infix($breakpoint, $screen-breakpoints); + + @include media-breakpoint-up($breakpoint, $screen-breakpoints) { + .ion-display#{$infix}-#{$display} { + display: #{$display} !important; + } + } + } +} diff --git a/core/src/css/test/display.e2e.ts b/core/src/css/test/display.e2e.ts new file mode 100644 index 00000000000..8d523e6cf64 --- /dev/null +++ b/core/src/css/test/display.e2e.ts @@ -0,0 +1,48 @@ +import { test, expect } from '@playwright/test'; +import fs from 'fs'; +import path from 'path'; + +test.describe('display css utility classes', () => { + let css: string; + + test.beforeAll(() => { + css = fs.readFileSync(path.resolve(__dirname, '../../../css/display.css'), 'utf8'); + }); + + const INFIXES = ['', '-sm', '-md', '-lg', '-xl']; + + // TODO(FW-6697): remove `ion-hide classes` test + test('ion-hide classes', () => { + expect(css).toContain('.ion-hide'); + + const values = ['up', 'down']; + + for (const value of values) { + for (const infix of INFIXES) { + expect(css).toContain(`.ion-hide${infix}-${value}`); + } + } + }); + + test('ion-display classes', () => { + const values = [ + 'none', + 'inline', + 'inline-block', + 'block', + 'flex', + 'inline-flex', + 'grid', + 'inline-grid', + 'table', + 'table-cell', + 'table-row', + ]; + + for (const value of values) { + for (const infix of INFIXES) { + expect(css).toContain(`.ion-display${infix}-${value}`); + } + } + }); +}); From 5aa655172d4cd0b85409abd8077e427c55ccae76 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:36:27 -0400 Subject: [PATCH 2/3] feat(css): add new flex utils css classes and test --- core/src/css/flex-utils.scss | 288 +++++++++++++++++++--------- core/src/css/test/flex-utils.e2e.ts | 100 ++++++++++ 2 files changed, 300 insertions(+), 88 deletions(-) create mode 100644 core/src/css/test/flex-utils.e2e.ts diff --git a/core/src/css/flex-utils.scss b/core/src/css/flex-utils.scss index 41e9f3325c4..f7a11f7e774 100644 --- a/core/src/css/flex-utils.scss +++ b/core/src/css/flex-utils.scss @@ -1,99 +1,211 @@ -// Flex Utilities -// -------------------------------------------------- -// Creates flex classes to align flex containers -// and items - -// Align Self -// -------------------------------------------------- - -.ion-align-self-start { - align-self: flex-start !important; -} - -.ion-align-self-end { - align-self: flex-end !important; -} - -.ion-align-self-center { - align-self: center !important; -} +@import "../themes/ionic.globals"; +@import "../themes/ionic.mixins"; -.ion-align-self-stretch { - align-self: stretch !important; -} - -.ion-align-self-baseline { - align-self: baseline !important; -} - -.ion-align-self-auto { - align-self: auto !important; -} - - -// Flex Wrap -// -------------------------------------------------- - -.ion-wrap { - flex-wrap: wrap !important; -} - -.ion-nowrap { - flex-wrap: nowrap !important; -} - -.ion-wrap-reverse { - flex-wrap: wrap-reverse !important; -} - - -// Justify Content -// -------------------------------------------------- - -.ion-justify-content-start { - justify-content: flex-start !important; -} - -.ion-justify-content-center { - justify-content: center !important; -} - -.ion-justify-content-end { - justify-content: flex-end !important; -} - -.ion-justify-content-around { - justify-content: space-around !important; -} - -.ion-justify-content-between { - justify-content: space-between !important; -} - -.ion-justify-content-evenly { - justify-content: space-evenly !important; +// Flex Utilities +// ------------------------------------------------------------------ +// Provides utility classes to control flexbox layout, alignment, +// and sizing of elements. Includes responsive variants for managing +// flex direction, alignment, justification, wrapping, growth, +// shrinking, and ordering at different breakpoints. + +// Align Content +// ------------------------------------------------------------------ + +$align-content-values: ( + start: flex-start, + end: flex-end, + center: center, + between: space-between, + around: space-around, + stretch: stretch +); + +@each $breakpoint in map-keys($screen-breakpoints) { + $infix: breakpoint-infix($breakpoint, $screen-breakpoints); + @include media-breakpoint-up($breakpoint, $screen-breakpoints) { + @each $key, $value in $align-content-values { + .ion-align-content#{$infix}-#{$key} { + align-content: #{$value} !important; + } + } + } } - // Align Items -// -------------------------------------------------- - -.ion-align-items-start { - align-items: flex-start !important; +// ------------------------------------------------------------------ + +$align-items-values: ( + start, + end, + center, + stretch, + baseline +); + +@each $breakpoint in map-keys($screen-breakpoints) { + $infix: breakpoint-infix($breakpoint, $screen-breakpoints); + @include media-breakpoint-up($breakpoint, $screen-breakpoints) { + @each $value in $align-items-values { + .ion-align-items#{$infix}-#{$value} { + align-items: #{$value} !important; + } + } + } } -.ion-align-items-center { - align-items: center !important; -} - -.ion-align-items-end { - align-items: flex-end !important; +// Align Self +// ------------------------------------------------------------------ + +$align-self-values: ( + start, + end, + center, + stretch, + baseline, + auto +); + +@each $breakpoint in map-keys($screen-breakpoints) { + $infix: breakpoint-infix($breakpoint, $screen-breakpoints); + @include media-breakpoint-up($breakpoint, $screen-breakpoints) { + @each $value in $align-self-values { + .ion-align-self#{$infix}-#{$value} { + align-self: #{$value} !important; + } + } + } } -.ion-align-items-stretch { - align-items: stretch !important; +// Justify Content +// ------------------------------------------------------------------ + +$justify-content-values: ( + start: flex-start, + end: flex-end, + center: center, + between: space-between, + around: space-around, + evenly: space-evenly +); + +@each $breakpoint in map-keys($screen-breakpoints) { + $infix: breakpoint-infix($breakpoint, $screen-breakpoints); + @include media-breakpoint-up($breakpoint, $screen-breakpoints) { + @each $key, $value in $justify-content-values { + .ion-justify-content#{$infix}-#{$key} { + justify-content: #{$value} !important; + } + } + } +} + +// Flex Direction +// ------------------------------------------------------------------ + +$flex-direction-values: ( + row, + row-reverse, + column, + column-reverse +); + +@each $breakpoint in map-keys($screen-breakpoints) { + $infix: breakpoint-infix($breakpoint, $screen-breakpoints); + @include media-breakpoint-up($breakpoint, $screen-breakpoints) { + @each $value in $flex-direction-values { + .ion-flex#{$infix}-#{$value} { + flex-direction: #{$value} !important; + } + } + } } -.ion-align-items-baseline { - align-items: baseline !important; +// Flex Wrap +// ------------------------------------------------------------------ + +$flex-wrap-values: ( + wrap, + nowrap, + wrap-reverse +); + +@each $value in $flex-wrap-values { + // TODO(FW-6697): remove ion-wrap, ion-nowrap, ion-wrap-reverse + // in favor of the new ion-flex-wrap, ion-flex-nowrap, and + // ion-flex-wrap-reverse classes + .ion-#{$value} { + flex-wrap: #{$value} !important; + } +} + +@each $breakpoint in map-keys($screen-breakpoints) { + $infix: breakpoint-infix($breakpoint, $screen-breakpoints); + @include media-breakpoint-up($breakpoint, $screen-breakpoints) { + @each $value in $flex-wrap-values { + .ion-flex#{$infix}-#{$value} { + flex-wrap: #{$value} !important; + } + } + } +} + +// Flex Fill +// ------------------------------------------------------------------ + +$flex-fill-values: ( + 1, + auto, + initial, + none +); + +@each $breakpoint in map-keys($screen-breakpoints) { + $infix: breakpoint-infix($breakpoint, $screen-breakpoints); + @include media-breakpoint-up($breakpoint, $screen-breakpoints) { + @each $value in $flex-fill-values { + .ion-flex#{$infix}-#{$value} { + flex: #{$value} !important; + } + } + } +} + +// Flex Grow and Shrink +// ------------------------------------------------------------------ + +@each $breakpoint in map-keys($screen-breakpoints) { + $infix: breakpoint-infix($breakpoint, $screen-breakpoints); + @include media-breakpoint-up($breakpoint, $screen-breakpoints) { + .ion-flex#{$infix}-grow-0 { + flex-grow: 0 !important; + } + + .ion-flex#{$infix}-grow-1 { + flex-grow: 1 !important; + } + + .ion-flex#{$infix}-shrink-0 { + flex-shrink: 0 !important; + } + + .ion-flex#{$infix}-shrink-1 { + flex-shrink: 1 !important; + } + } +} + +// Flex Order +// ------------------------------------------------------------------ + +@each $breakpoint in map-keys($screen-breakpoints) { + $infix: breakpoint-infix($breakpoint, $screen-breakpoints); + @include media-breakpoint-up($breakpoint, $screen-breakpoints) { + .ion-order#{$infix}-first { order: -1 !important; } + + @for $i from 0 through 12 { + .ion-order#{$infix}-#{$i} { order: #{$i} !important; } + } + + .ion-order#{$infix}-last { order: 13 !important; } + } } diff --git a/core/src/css/test/flex-utils.e2e.ts b/core/src/css/test/flex-utils.e2e.ts new file mode 100644 index 00000000000..d691ae9f558 --- /dev/null +++ b/core/src/css/test/flex-utils.e2e.ts @@ -0,0 +1,100 @@ +import { test, expect } from '@playwright/test'; +import fs from 'fs'; +import path from 'path'; + +test.describe('flex-utils css utility classes', () => { + let css: string; + + test.beforeAll(() => { + css = fs.readFileSync(path.resolve(__dirname, '../../../css/flex-utils.css'), 'utf8'); + }); + + const INFIXES = ['', '-sm', '-md', '-lg', '-xl']; + + test('align-content classes', () => { + const values = ['start', 'end', 'center', 'between', 'around', 'stretch']; + for (const value of values) { + for (const infix of INFIXES) { + expect(css).toContain(`.ion-align-content${infix}-${value}`); + } + } + }); + + test('align-items classes', () => { + const values = ['start', 'center', 'end', 'stretch', 'baseline']; + for (const value of values) { + for (const infix of INFIXES) { + expect(css).toContain(`.ion-align-items${infix}-${value}`); + } + } + }); + + test('align-self classes', () => { + const values = ['start', 'end', 'center', 'stretch', 'baseline', 'auto']; + for (const value of values) { + for (const infix of INFIXES) { + expect(css).toContain(`.ion-align-self${infix}-${value}`); + } + } + }); + + test('justify-content classes', () => { + const values = ['start', 'center', 'end', 'around', 'between', 'evenly']; + for (const value of values) { + for (const infix of INFIXES) { + expect(css).toContain(`.ion-justify-content${infix}-${value}`); + } + } + }); + + test('flex-direction classes', () => { + const values = ['row', 'row-reverse', 'column', 'column-reverse']; + for (const value of values) { + for (const infix of INFIXES) { + expect(css).toContain(`.ion-flex${infix}-${value}`); + } + } + }); + + test('flex-wrap classes', () => { + const values = ['wrap', 'nowrap', 'wrap-reverse']; + // TODO(FW-6697): remove all `ion-wrap-*` expects + for (const value of values) { + expect(css).toContain(`.ion-${value}`); + } + for (const value of values) { + for (const infix of INFIXES) { + expect(css).toContain(`.ion-flex${infix}-${value}`); + } + } + }); + + test('flex-fill classes', () => { + const values = ['1', 'auto', 'initial', 'none']; + for (const value of values) { + for (const infix of INFIXES) { + expect(css).toContain(`.ion-flex${infix}-${value}`); + } + } + }); + + test('flex-grow and flex-shrink classes', () => { + const values = ['grow', 'shrink']; + for (const value of values) { + for (const infix of INFIXES) { + expect(css).toContain(`.ion-flex${infix}-${value}-0`); + expect(css).toContain(`.ion-flex${infix}-${value}-1`); + } + } + }); + + test('flex-order classes', () => { + for (const infix of INFIXES) { + expect(css).toContain(`.ion-order${infix}-first`); + expect(css).toContain(`.ion-order${infix}-last`); + for (let i = 0; i <= 12; i++) { + expect(css).toContain(`.ion-order${infix}-${i}`); + } + } + }); +}); From ff0d215ce4763e52b6054c512e211f0da5710db2 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Fri, 25 Jul 2025 14:02:02 -0400 Subject: [PATCH 3/3] style: document the requirement to remove the ion-hide class --- core/src/components/back-button/test/basic/index.html | 2 +- core/src/components/progress-bar/progress-bar.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/components/back-button/test/basic/index.html b/core/src/components/back-button/test/basic/index.html index c43335fb69b..af2d4ef59f1 100644 --- a/core/src/components/back-button/test/basic/index.html +++ b/core/src/components/back-button/test/basic/index.html @@ -125,7 +125,7 @@

Custom

- + Hidden diff --git a/core/src/components/progress-bar/progress-bar.tsx b/core/src/components/progress-bar/progress-bar.tsx index 22e2f00d790..12ccd66dec9 100644 --- a/core/src/components/progress-bar/progress-bar.tsx +++ b/core/src/components/progress-bar/progress-bar.tsx @@ -107,6 +107,7 @@ const renderProgress = (value: number, buffer: number) => { * When finalBuffer === 1, we use display: none * instead of removing the element to avoid flickering. */ + // TODO(FW-6697): change `ion-hide` class to `ion-display-none` or another class