mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(card): convert card-content to shadow DOM (#30759)
Issue number: internal
---------
## What is the current behavior?
Card content has no encapsulation.
## What is the new behavior?
Converted `ion-card-content` to Shadow DOM which improves consistency among components & CSP compatibility.
## Does this introduce a breaking change?
- [x] Yes
- [ ] No
BREAKING CHANGE:
The `ion-card-content` component has been updated to Shadow DOM. With this update, all card-related components now use Shadow DOM for style encapsulation. There should not be any breaking changes related to targeting inner elements since `ion-card-content` does not have any internal elements of its own. However, some user styles may break due to the removal of the `card-content-{mode}` class or changes in selector specificity.
The default styles for heading elements inside `ion-card-content` have been removed. If you need custom styling for headings, you can add your own CSS targeting these elements.
For example:
```css
ion-card-content h1 {
margin-top: 0;
margin-bottom: 2px;
font-size: 1.5rem;
}
ion-card-content h2 {
margin-top: 2px;
margin-bottom: 2px;
font-size: 1rem;
}
ion-card-content h3,
ion-card-content h4,
ion-card-content h5,
ion-card-content h6 {
margin-top: 2px;
margin-bottom: 2px;
font-size: 0.875rem;
}
```
---------
Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
This commit is contained in:
46
BREAKING.md
46
BREAKING.md
@@ -28,7 +28,35 @@ This is a comprehensive list of the breaking changes introduced in the major ver
|
||||
|
||||
<h4 id="version-9x-card">Card</h4>
|
||||
|
||||
- The `border-radius` of the `ios` and `md` card now defaults to `14px` and `12px` instead of `8px` and `4px`, respectively, in accordance with the iOS and Material Design 3 guidelines. To revert to the previous appearance, set the `shape` to `"soft"`, or override the `--border-radius` CSS variable to specify a different value.
|
||||
- **ion-card**: The `border-radius` of the `ios` and `md` card now defaults to `14px` and `12px` instead of `8px` and `4px`, respectively, in accordance with the iOS and Material Design 3 guidelines. To revert to the previous appearance, set the `shape` to `"soft"`, or override the `--border-radius` CSS variable to specify a different value.
|
||||
|
||||
- **ion-card-content**: The `ion-card-content` component has been updated to Shadow DOM. With this update, all card-related components now use Shadow DOM for style encapsulation. The default styles for heading elements inside `ion-card-content` have been removed. If you need custom styling for headings, you can add your own CSS targeting these elements. For example:
|
||||
|
||||
```css
|
||||
ion-card-content h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 2px;
|
||||
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
ion-card-content h2 {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
ion-card-content h3,
|
||||
ion-card-content h4,
|
||||
ion-card-content h5,
|
||||
ion-card-content h6 {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
```
|
||||
|
||||
<h4 id="version-9x-chip">Chip</h4>
|
||||
|
||||
@@ -47,6 +75,7 @@ Additionally, the `radio-group-wrapper` div element has been removed, causing sl
|
||||
<h5>Example 1: Swap two columns</h5>
|
||||
|
||||
**Version up to 8.x**
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -56,7 +85,9 @@ Additionally, the `radio-group-wrapper` div element has been removed, causing sl
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
|
||||
**Version 9.x+**
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -68,9 +99,11 @@ Additionally, the `radio-group-wrapper` div element has been removed, causing sl
|
||||
```
|
||||
|
||||
<h5>Example 2: Reorder columns with specific sizes</h5>
|
||||
|
||||
To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `size="3" pull="9"`:
|
||||
|
||||
**Version up to 8.x**
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -79,7 +112,9 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
|
||||
**Version 9.x+**
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -88,7 +123,9 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
|
||||
<h5>Example 3: Push</h5>
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -101,7 +138,9 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
|
||||
**Version 9.x+**
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -116,6 +155,7 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
|
||||
```
|
||||
|
||||
<h5>Example 4: Push and Pull</h5>
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -128,7 +168,9 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
|
||||
**Version 9.x+**
|
||||
|
||||
```html
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
@@ -140,4 +182,4 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
```
|
||||
```
|
||||
|
||||
@@ -504,7 +504,7 @@ ion-card,css-prop,--color,ios
|
||||
ion-card,css-prop,--color,md
|
||||
ion-card,part,native
|
||||
|
||||
ion-card-content,none
|
||||
ion-card-content,shadow
|
||||
ion-card-content,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-card-content,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Card Content
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-card-content {
|
||||
:host {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// Ionic Card Content
|
||||
// --------------------------------------------------
|
||||
|
||||
.card-content-ionic {
|
||||
:host {
|
||||
@include globals.padding(globals.$ion-space-400);
|
||||
@include globals.typography(globals.$ion-body-md-regular);
|
||||
|
||||
@@ -13,12 +13,8 @@
|
||||
flex-direction: column;
|
||||
|
||||
gap: globals.$ion-space-400;
|
||||
|
||||
img {
|
||||
@include globals.margin(globals.$ion-space-200, 0, globals.$ion-space-200, 0);
|
||||
}
|
||||
}
|
||||
|
||||
ion-card-header + .card-content-ionic {
|
||||
padding-top: 0;
|
||||
::slotted(img) {
|
||||
@include globals.margin(globals.$ion-space-200, 0, globals.$ion-space-200, 0);
|
||||
}
|
||||
|
||||
@@ -4,44 +4,16 @@
|
||||
// iOS Card Header
|
||||
// --------------------------------------------------
|
||||
|
||||
.card-content-ios {
|
||||
:host {
|
||||
@include padding($card-ios-padding-top, $card-ios-padding-end, $card-ios-padding-bottom, $card-ios-padding-start);
|
||||
|
||||
font-size: $card-ios-font-size;
|
||||
|
||||
line-height: 1.4;
|
||||
|
||||
h1 {
|
||||
@include margin(0, 0, 2px);
|
||||
|
||||
font-size: dynamic-font(24px);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@include margin(2px, 0);
|
||||
|
||||
font-size: dynamic-font(16px);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
@include margin(2px, 0);
|
||||
|
||||
font-size: dynamic-font(14px);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
p {
|
||||
@include margin(0, 0, 2px);
|
||||
|
||||
font-size: dynamic-font(14px);
|
||||
}
|
||||
}
|
||||
|
||||
ion-card-header + .card-content-ios {
|
||||
padding-top: 0;
|
||||
::slotted(p) {
|
||||
@include margin(0, 0, 2px);
|
||||
|
||||
font-size: dynamic-font(14px);
|
||||
}
|
||||
|
||||
@@ -4,47 +4,19 @@
|
||||
// Material Design Card Content
|
||||
// --------------------------------------------------
|
||||
|
||||
.card-content-md {
|
||||
:host {
|
||||
@include padding($card-md-padding-top, $card-md-padding-end, $card-md-padding-bottom, $card-md-padding-start);
|
||||
|
||||
font-size: $card-md-font-size;
|
||||
|
||||
line-height: $card-md-line-height;
|
||||
|
||||
h1 {
|
||||
@include margin(0, 0, 2px);
|
||||
|
||||
font-size: dynamic-font(24px);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@include margin(2px, 0);
|
||||
|
||||
font-size: dynamic-font(16px);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
@include margin(2px, 0);
|
||||
|
||||
font-size: dynamic-font(14px);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
p {
|
||||
@include margin(0, 0, 2px);
|
||||
|
||||
font-size: dynamic-font(14px);
|
||||
font-weight: normal;
|
||||
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
ion-card-header + .card-content-md {
|
||||
padding-top: 0;
|
||||
::slotted(p) {
|
||||
@include margin(0, 0, 2px);
|
||||
|
||||
font-size: dynamic-font(14px);
|
||||
font-weight: normal;
|
||||
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
// Card Content
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-card-content {
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { getIonTheme } from '../../global/ionic-global';
|
||||
md: 'card-content.md.scss',
|
||||
ionic: 'card-content.ionic.scss',
|
||||
},
|
||||
shadow: true,
|
||||
})
|
||||
export class CardContent implements ComponentInterface {
|
||||
render() {
|
||||
@@ -22,11 +23,10 @@ export class CardContent implements ComponentInterface {
|
||||
<Host
|
||||
class={{
|
||||
[theme]: true,
|
||||
|
||||
// Used internally for styling
|
||||
[`card-content-${theme}`]: true,
|
||||
}}
|
||||
></Host>
|
||||
>
|
||||
<slot></slot>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,27 +91,6 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
const card = page.locator('ion-card');
|
||||
await expect(card).toHaveScreenshot(screenshot(`card-color`));
|
||||
});
|
||||
test('headings should have correct size in card', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-card>
|
||||
<ion-card-content>
|
||||
<h1>Heading 1</h1>
|
||||
<h2>Heading 2</h2>
|
||||
<h3>Heading 3</h3>
|
||||
<h4>Heading 4</h4>
|
||||
<h5>Heading 5</h5>
|
||||
<h6>Heading 6</h6>
|
||||
<p>Paragraph</p>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const card = page.locator('ion-card');
|
||||
await expect(card).toHaveScreenshot(screenshot(`card-headings`));
|
||||
});
|
||||
test('should render even without header or content elements', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 7.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.0 KiB |
@@ -244,6 +244,10 @@ ion-card-header.ion-color .ion-inherit-color {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
ion-card-header + ion-card-content {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
// Menu Styles
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
@@ -238,6 +238,10 @@ ion-card-header.ion-color .ion-inherit-color {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
ion-card-header + ion-card-content {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
// Menu Styles
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user