Ionic Framework components differ in attributes, but item components usually have a slot attribute. Let's look at what that is and how it is implemented.
slot as Web Components
Ionic components have many custom attributes like color and mode, but the slot attribute is the standard spec for inserting DOM into the slot element defined in Web Components Shadow DOM.
https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/slot
For Ionic Framework's IonItem, the template looks like this (only the slot-related parts are shown; the rest is omitted):
<ion-item>
<slot name="start"></slot>
<div class="item-inner">
<div class="input-wrapper">
<slot></slot>
</div>
<slot name="end"></slot>
</div>
</ion-item>
Everything under ion-item is Shadow DOM. You use the template like this:
<ion-item>
<ion-icon name="close" slot="start"></ion-icon>
<ion-label>
<h2>H2 Title Text</h2>
<p>Button on right</p>
</ion-label>
<ion-button fill="outline" slot="end">View</ion-button>
</ion-item>
Then the HTML above is inserted where matching slot[name] slots are. In other words:
ion-thumbnail[slot=start]goes inion-item > slot[name=start]ion-button[slot=end]goes inion-item > .item-inner > slot[name=end]ion-labelwithout a slot goes inion-item > .item-inner > .input-wrapper > slot
Ionic Framework is built on web standards, so this follows the Web Components spec as-is.
Styling slot
Let's briefly look at how styling works. In CSS, slotted content is targeted with ::slotted(). Part of the styling above looks like this:
<ion-item>
<slot name="start"></slot>
<div class="item-inner">
<div class="input-wrapper">
<slot></slot>
</div>
<slot name="end"></slot>
</div>
</ion-item>
::slotted(ion-icon) {
font-size: 1.6em;
}
::slotted(ion-button) {
--margin-top: 0;
--margin-bottom: 0;
--margin-start: 0;
--margin-end: 0;
z-index: 1;
}
::slotted(ion-label) {
flex: 1;
}
Slotted ion-icon gets font-size: 1.6em. For ion-button, default margins are zeroed out, and ion-label gets flex: 1 because of the parent .input-wrapperβfor when there are multiple ion-label elements.
The heart of this design is .item-inner and .input-wrapper (partially omitted):
.item-inner {
display: flex;
// This is required to work with an inset highlight
position: relative;
flex: 1;
flex-direction: inherit;
align-items: inherit;
align-self: stretch;
}
.input-wrapper {
display: flex;
flex: 1;
flex-direction: inherit;
align-items: inherit;
align-self: stretch;
text-overflow: ellipsis;
overflow: inherit;
box-sizing: border-box;
}
Elements with [slot=start] are usually width-capped items (ion-icon, ion-button, and so on), so the unnamed slot needs to fill the remaining space. That is why .item-inner has align-self: stretch. The same applies to input-wrapper under .item-inner.
This yields a structure where "[slot=start] takes space first, then [slot=end] inside .item-inner, and the rest goes to the unnamed slot."
Summary
When designing a framework, this is a good example of parent-child relationships and use cases being thought through carefully. Reading the source reveals design intent like this, so if you want to go deeper, read the code.
https://github.com/ionic-team/ionic-framework/blob/master/core/src/components/item/item.scss
See you again.