Compare commits

..

13 Commits

Author SHA1 Message Date
Maria Hutt
77e50df450 feat(item-divider): add interface 2026-03-12 13:47:42 -07:00
Maria Hutt
fd9f0b5bd0 feat(item-divider): revert-layer note font size 2026-03-12 12:04:53 -07:00
Maria Hutt
6260629a99 docs(item-divider): update comments 2026-03-12 11:56:50 -07:00
Maria Hutt
d9db79f729 docs(item-divider, thumbnail): update todo 2026-03-12 11:40:45 -07:00
Maria Hutt
87e6443c18 test(item): update divider snapshots again 2026-03-12 11:38:44 -07:00
Maria Hutt
eac584c6fd test(item): update divider snapshots 2026-03-12 11:36:00 -07:00
Maria Hutt
fda52f5fde docs(tokens): update shared file comments 2026-03-12 11:05:30 -07:00
Maria Hutt
c66561e38b refactor(tokens): rearrange shared tokens 2026-03-12 11:01:57 -07:00
Maria Hutt
4b170f0303 docs(item-divider): add todo for border 2026-03-12 09:37:37 -07:00
Maria Hutt
e2cf0e4278 feat(item-divider): remove theme usage 2026-03-12 09:31:30 -07:00
Maria Hutt
38145a3b3e chore(item-divider): remove theme sass files 2026-03-12 09:27:21 -07:00
Maria Hutt
8295819513 chore(): run build 2026-03-11 18:20:34 -07:00
Maria Hutt
20a4a3370c feat(item-divider): add recipe and tokens 2026-03-11 16:15:19 -07:00
35 changed files with 1677 additions and 414 deletions

View File

@@ -1244,37 +1244,16 @@ ion-item-divider,shadow
ion-item-divider,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-item-divider,prop,mode,"ios" | "md",undefined,false,false
ion-item-divider,prop,sticky,boolean,false,false,false
ion-item-divider,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-item-divider,css-prop,--background,ionic
ion-item-divider,css-prop,--background,ios
ion-item-divider,css-prop,--background,md
ion-item-divider,css-prop,--color,ionic
ion-item-divider,css-prop,--color,ios
ion-item-divider,css-prop,--color,md
ion-item-divider,css-prop,--inner-padding-bottom,ionic
ion-item-divider,css-prop,--inner-padding-bottom,ios
ion-item-divider,css-prop,--inner-padding-bottom,md
ion-item-divider,css-prop,--inner-padding-end,ionic
ion-item-divider,css-prop,--inner-padding-end,ios
ion-item-divider,css-prop,--inner-padding-end,md
ion-item-divider,css-prop,--inner-padding-start,ionic
ion-item-divider,css-prop,--inner-padding-start,ios
ion-item-divider,css-prop,--inner-padding-start,md
ion-item-divider,css-prop,--inner-padding-top,ionic
ion-item-divider,css-prop,--inner-padding-top,ios
ion-item-divider,css-prop,--inner-padding-top,md
ion-item-divider,css-prop,--padding-bottom,ionic
ion-item-divider,css-prop,--padding-bottom,ios
ion-item-divider,css-prop,--padding-bottom,md
ion-item-divider,css-prop,--padding-end,ionic
ion-item-divider,css-prop,--padding-end,ios
ion-item-divider,css-prop,--padding-end,md
ion-item-divider,css-prop,--padding-start,ionic
ion-item-divider,css-prop,--padding-start,ios
ion-item-divider,css-prop,--padding-start,md
ion-item-divider,css-prop,--padding-top,ionic
ion-item-divider,css-prop,--padding-top,ios
ion-item-divider,css-prop,--padding-top,md
ion-item-divider,css-prop,--background
ion-item-divider,css-prop,--color
ion-item-divider,css-prop,--inner-padding-bottom
ion-item-divider,css-prop,--inner-padding-end
ion-item-divider,css-prop,--inner-padding-start
ion-item-divider,css-prop,--inner-padding-top
ion-item-divider,css-prop,--padding-bottom
ion-item-divider,css-prop,--padding-end
ion-item-divider,css-prop,--padding-start
ion-item-divider,css-prop,--padding-top
ion-item-group,none
ion-item-group,prop,mode,"ios" | "md",undefined,false,false

View File

@@ -1920,10 +1920,6 @@ export namespace Components {
* @default false
*/
"sticky": boolean;
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonItemGroup {
/**
@@ -7918,10 +7914,6 @@ declare namespace LocalJSX {
* @default false
*/
"sticky"?: boolean;
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonItemGroup {
/**

View File

@@ -1,5 +1,4 @@
@use "../../themes/mixins" as mixins;
@use "../../themes/functions.color" as colors;
// Chip: Common Styles
// --------------------------------------------------

View File

@@ -0,0 +1,185 @@
import type { IonPadding, IonMargin } from '../../themes/themes.interfaces';
export type IonItemDividerRecipe = {
background?: string;
color?: string;
minHeight?: string;
zIndex?: number;
padding?: IonPadding;
inner?: {
padding?: IonPadding;
};
border?: {
bottom?: string;
};
font?: {
size?: string;
weight?: string;
};
leading?: {
anchor?: {
margin?: IonMargin;
};
edge?: {
margin?: IonMargin;
};
};
trailing?: {
edge?: {
margin?: IonMargin;
};
};
label?: {
margin?: IonMargin;
};
icon?: {
font?: {
size?: string;
};
leading?: {
edge?: {
margin?: IonMargin;
};
};
trailing?: {
edge?: {
margin?: IonMargin;
};
};
default?: {
color?: string;
};
semantic?: {
default?: {
color?: string;
};
};
};
note?: {
align?: {
self?: string;
};
font?: {
size?: string;
};
margin?: IonMargin;
padding?: IonPadding;
};
avatar?: {
height?: string;
width?: string;
margin?: IonMargin;
leading?: {
edge?: {
margin?: IonMargin;
};
};
trailing?: {
edge?: {
margin?: IonMargin;
};
};
};
thumbnail?: {
height?: string;
width?: string;
margin?: IonMargin;
leading?: {
edge?: {
margin?: IonMargin;
};
};
trailing?: {
edge?: {
margin?: IonMargin;
};
};
};
header1?: {
margin?: IonMargin;
};
header2?: {
margin?: IonMargin;
trailing?: {
margin?: IonMargin;
};
};
header3?: {
margin?: IonMargin;
trailing?: {
margin?: IonMargin;
};
};
header4?: {
margin?: IonMargin;
trailing?: {
margin?: IonMargin;
};
};
header5?: {
margin?: IonMargin;
trailing?: {
margin?: IonMargin;
};
};
header6?: {
margin?: IonMargin;
trailing?: {
margin?: IonMargin;
};
};
paragraph?: {
color?: string;
overflow?: string;
margin?: IonMargin;
font?: {
size?: string;
};
text?: {
overflow?: string;
};
trailing?: {
margin?: IonMargin;
};
};
};

View File

@@ -1,98 +0,0 @@
@import "./item-divider";
@import "./item-divider.ios.vars";
// iOS Item Divider
// --------------------------------------------------
:host {
--background: #{$item-divider-ios-background};
--color: #{$item-divider-ios-color};
--padding-start: #{$item-divider-ios-padding-start};
--inner-padding-end: #{$item-divider-ios-padding-end * 0.5};
@include border-radius(0);
position: relative;
min-height: $item-divider-ios-min-height;
font-size: $item-divider-ios-font-size;
font-weight: $item-divider-ios-font-weight;
}
// iOS Item Divider Slots
// --------------------------------------------------
:host([slot="start"]) {
@include margin(
$item-ios-slot-start-margin-top,
$item-ios-slot-start-margin-end,
$item-ios-slot-start-margin-bottom,
$item-ios-slot-start-margin-start
);
}
:host([slot="end"]) {
@include margin(
$item-ios-slot-end-margin-top,
$item-ios-slot-end-margin-end,
$item-ios-slot-end-margin-bottom,
$item-ios-slot-end-margin-start
);
}
::slotted(ion-icon[slot="start"]),
::slotted(ion-icon[slot="end"]) {
@include margin(
$item-ios-icon-slot-margin-top,
$item-ios-icon-slot-margin-end,
$item-ios-icon-slot-margin-bottom,
$item-ios-icon-slot-margin-start
);
}
// iOS Item Divider Content
// --------------------------------------------------
::slotted(h1) {
@include margin(0, 0, 2px);
}
::slotted(h2) {
@include margin(0, 0, 2px);
}
::slotted(h3),
::slotted(h4),
::slotted(h5),
::slotted(h6) {
@include margin(0, 0, 3px);
}
::slotted(p) {
@include margin(
$item-ios-paragraph-margin-top,
$item-ios-paragraph-margin-end,
$item-ios-paragraph-margin-bottom,
$item-ios-paragraph-margin-start
);
color: $item-ios-paragraph-text-color;
font-size: $item-ios-paragraph-font-size;
line-height: normal;
text-overflow: inherit;
overflow: inherit;
}
::slotted(h2:last-child) ::slotted(h3:last-child),
::slotted(h4:last-child),
::slotted(h5:last-child),
::slotted(h6:last-child),
::slotted(p:last-child) {
@include margin(null, null, 0, null);
}

View File

@@ -1,26 +0,0 @@
@import "../../themes/native/native.globals.ios";
@import "../item/item.ios.vars";
// iOS Item Divider
// --------------------------------------------------
/// @prop - Minimum height for the divider
$item-divider-ios-min-height: 28px;
/// @prop - Font size of the item
$item-divider-ios-font-size: dynamic-font(17px);
/// @prop - Font weight of the item
$item-divider-ios-font-weight: 600;
/// @prop - Background for the divider
$item-divider-ios-background: $background-color-step-100;
/// @prop - Color for the divider
$item-divider-ios-color: $text-color-step-150;
/// @prop - Padding start for the divider
$item-divider-ios-padding-start: $item-ios-padding-start;
/// @prop - Padding end for the divider
$item-divider-ios-padding-end: $item-ios-padding-end;

View File

@@ -1,160 +0,0 @@
@use "sass:math";
@import "./item-divider";
@import "./item-divider.md.vars";
// Material Design Item Divider
// --------------------------------------------------
:host {
--background: #{$item-divider-md-background};
--color: #{$item-divider-md-color};
--padding-start: #{$item-divider-md-padding-start};
--inner-padding-end: #{$item-divider-md-padding-end};
min-height: $item-divider-md-min-height;
border-bottom: $item-divider-md-border-bottom;
font-size: dynamic-font($item-divider-md-font-size);
}
// Material Design Item Divider Slots
// --------------------------------------------------
::slotted([slot="start"]) {
@include margin-horizontal($item-md-start-slot-margin-start, $item-md-start-slot-margin-end);
}
::slotted([slot="end"]) {
@include margin-horizontal($item-md-end-slot-margin-start, $item-md-end-slot-margin-end);
}
// Material Design Slotted Label
// --------------------------------------------------
::slotted(ion-label) {
@include margin(13px, 0, 10px, 0);
}
// Material Design Slotted Icon
// --------------------------------------------------
::slotted(ion-icon) {
color: $item-md-icon-slot-color;
// The icon's font size should use em units to support
// font scaling but evaluate to 24px at 100% font size.
// The value in em units is calculated by dividing
// the icon's font size in pixels by the item divider's
// font size in pixels.
// e.g. 24px / 14px = 1.7142857143em
font-size: math.div($item-md-icon-slot-font-size, $item-divider-md-font-size) * 1em;
}
:host(.ion-color) ::slotted(ion-icon) {
color: current-color(contrast);
}
::slotted(ion-icon[slot]) {
@include margin(
$item-md-icon-slot-margin-top,
$item-md-icon-slot-margin-end,
$item-md-icon-slot-margin-bottom,
$item-md-icon-slot-margin-start
);
}
::slotted(ion-icon[slot="start"]) {
@include margin-horizontal($item-md-icon-start-slot-margin-start, $item-md-icon-start-slot-margin-end);
}
::slotted(ion-icon[slot="end"]) {
@include margin-horizontal($item-md-icon-end-slot-margin-start, $item-md-icon-end-slot-margin-end);
}
// Material Design Slotted Note
// --------------------------------------------------
::slotted(ion-note) {
@include margin(0);
align-self: flex-start;
font-size: $item-md-note-slot-font-size;
}
::slotted(ion-note[slot]) {
@include padding(
$item-md-note-slot-padding-top,
$item-md-note-slot-padding-end,
$item-md-note-slot-padding-bottom,
$item-md-note-slot-padding-start
);
}
// Material Design Item Divider Avatar
// --------------------------------------------------
::slotted(ion-avatar) {
width: $item-md-avatar-width;
height: $item-md-avatar-height;
}
// Material Design Item Divider Thumbnail
// --------------------------------------------------
::slotted(ion-thumbnail) {
--size: #{$item-md-thumbnail-size};
}
// Material Design Item Divider Avatar/Thumbnail
// --------------------------------------------------
::slotted(ion-avatar),
::slotted(ion-thumbnail) {
@include margin(
$item-md-media-slot-margin-top,
$item-md-media-slot-margin-end,
$item-md-media-slot-margin-bottom,
$item-md-media-slot-margin-start
);
}
::slotted(ion-avatar[slot="start"]),
::slotted(ion-thumbnail[slot="start"]) {
@include margin-horizontal($item-md-media-start-slot-margin-start, $item-md-media-start-slot-margin-end);
}
::slotted(ion-avatar[slot="end"]),
::slotted(ion-thumbnail[slot="end"]) {
@include margin-horizontal($item-md-media-end-slot-margin-start, $item-md-media-end-slot-margin-end);
}
// Material Design Item Divider Content
// --------------------------------------------------
::slotted(h1) {
@include margin(0, 0, 2px);
}
::slotted(h2) {
@include margin(2px, 0);
}
::slotted(h3, h4, h5, h6) {
@include margin(2px, 0);
}
::slotted(p) {
@include margin(0, 0, 2px);
color: $item-md-paragraph-text-color;
font-size: dynamic-font(14px);
line-height: normal;
text-overflow: inherit;
overflow: inherit;
}

View File

@@ -1,26 +0,0 @@
@import "../../themes/native/native.globals.md";
@import "../item/item.md.vars";
// Material Design Item Divider
// --------------------------------------------------
/// @prop - Minimum height for the divider
$item-divider-md-min-height: 30px;
/// @prop - Color for the divider
$item-divider-md-color: $text-color-step-600;
/// @prop - Background for the divider
$item-divider-md-background: $background-color;
/// @prop - Font size for the divider
$item-divider-md-font-size: 14px;
/// @prop - Border bottom for the divider
$item-divider-md-border-bottom: 1px solid $item-md-border-color;
/// @prop - Padding start for the divider
$item-divider-md-padding-start: $item-md-padding-start;
/// @prop - Padding end for the divider
$item-divider-md-padding-end: $item-md-padding-end;

View File

@@ -1,6 +1,7 @@
@import "../../themes/native/native.globals";
@use "../../themes/mixins" as mixins;
@use "../../themes/functions.color" as colors;
// Item Divider
// Item Divider: Common Styles
// --------------------------------------------------
:host {
@@ -19,26 +20,29 @@
* @prop --inner-padding-bottom: Bottom inner padding of the item divider
* @prop --inner-padding-start: Start inner padding of the item divider
*/
--padding-top: 0px;
--padding-end: 0px;
--padding-bottom: 0px;
--padding-start: 0px;
--inner-padding-top: 0px;
--inner-padding-end: 0px;
--inner-padding-bottom: 0px;
--inner-padding-start: 0px;
--background: var(--ion-item-divider-background);
--color: var(--ion-item-divider-color);
--padding-top: var(--ion-item-divider-padding-top);
--padding-end: var(--ion-item-divider-padding-end);
--padding-bottom: var(--ion-item-divider-padding-bottom);
--padding-start: var(--ion-item-divider-padding-start);
--inner-padding-top: var(--ion-item-divider-inner-padding-top);
--inner-padding-end: var(--ion-item-divider-inner-padding-end);
--inner-padding-bottom: var(--ion-item-divider-inner-padding-bottom);
--inner-padding-start: var(--ion-item-divider-inner-padding-start);
@include font-smoothing();
@include margin(0);
@include padding(var(--padding-top), null, var(--padding-bottom), null);
@include mixins.font-smoothing();
@include mixins.margin(0);
@include mixins.padding(var(--padding-top), null, var(--padding-bottom), null);
@include mixins.border-radius(0);
/* stylelint-disable */
@include ltr() {
@include mixins.ltr() {
padding-right: var(--padding-end);
padding-left: calc(var(--padding-start) + var(--ion-safe-area-left, 0px));
}
@include rtl() {
@include mixins.rtl() {
padding-right: calc(var(--padding-start) + var(--ion-safe-area-right, 0px));
padding-left: var(--padding-end);
}
@@ -51,19 +55,27 @@
width: 100%;
min-height: var(--ion-item-divider-min-height);
// TODO(FW-6848): remove border-bottom and replace it with the `ion-divider`
border-bottom: var(--ion-item-divider-border-bottom);
background: var(--background);
color: var(--color);
font-family: $font-family-base;
font-family: var(--ion-font-family, inherit);
font-size: var(--ion-item-divider-font-size);
font-weight: var(--ion-item-divider-font-weight);
overflow: hidden;
z-index: $z-index-item-divider;
z-index: var(--ion-item-divider-z-index, auto);
box-sizing: border-box;
}
:host(.ion-color) {
background: current-color(base);
color: current-color(contrast);
background: colors.current-color("base");
color: colors.current-color("contrast");
}
:host(.item-divider-sticky) {
@@ -72,16 +84,16 @@
}
.item-divider-inner {
@include margin(0);
@include padding(var(--inner-padding-top), null, var(--inner-padding-bottom), null);
@include mixins.margin(0);
@include mixins.padding(var(--inner-padding-top), null, var(--inner-padding-bottom), null);
/* stylelint-disable */
@include ltr() {
@include mixins.ltr() {
padding-right: calc(var(--ion-safe-area-right, 0px) + var(--inner-padding-end));
padding-left: var(--inner-padding-start);
}
@include rtl() {
@include mixins.rtl() {
padding-right: var(--inner-padding-start);
padding-left: calc(var(--ion-safe-area-left, 0px) + var(--inner-padding-end));
}
@@ -113,3 +125,277 @@
overflow: hidden;
}
// Item Divider Slots
// --------------------------------------------------
:host([slot="start"]) {
@include mixins.margin(
var(--ion-item-divider-leading-anchor-margin-top, revert-layer),
var(--ion-item-divider-leading-anchor-margin-end, revert-layer),
var(--ion-item-divider-leading-anchor-margin-bottom, revert-layer),
var(--ion-item-divider-leading-anchor-margin-start, revert-layer)
);
}
// Item Divider Slotted Elements
// ---------------------------------------------
// Slotted Start
::slotted([slot="start"]) {
@include mixins.margin-horizontal(
var(--ion-item-divider-leading-edge-margin-start, revert-layer),
var(--ion-item-divider-leading-edge-margin-end, revert-layer)
); // for ionic this should be 0
}
// Slotted End
::slotted([slot="end"]) {
@include mixins.margin-horizontal(
var(--ion-item-divider-trailing-edge-margin-start, revert-layer),
var(--ion-item-divider-trailing-edge-margin-end, revert-layer)
);
}
// Label
::slotted(ion-label) {
@include mixins.margin(
var(--ion-item-divider-label-margin-top),
var(--ion-item-divider-label-margin-end),
var(--ion-item-divider-label-margin-bottom),
var(--ion-item-divider-label-margin-start)
);
}
// Icon
::slotted(ion-icon) {
color: var(--ion-item-divider-icon-default-color);
font-size: var(--ion-item-divider-icon-font-size);
}
:host(.ion-color) ::slotted(ion-icon) {
color: var(--ion-item-divider-icon-semantic-default-color);
}
::slotted(ion-icon[slot="start"]) {
@include mixins.margin(
var(--ion-item-divider-icon-leading-edge-margin-top, revert-layer),
var(--ion-item-divider-icon-leading-edge-margin-end, revert-layer),
var(--ion-item-divider-icon-leading-edge-margin-bottom, revert-layer),
var(--ion-item-divider-icon-leading-edge-margin-start, revert-layer)
);
}
::slotted(ion-icon[slot="end"]) {
@include mixins.margin(
var(--ion-item-divider-icon-trailing-edge-margin-top, revert-layer),
var(--ion-item-divider-icon-trailing-edge-margin-end, revert-layer),
var(--ion-item-divider-icon-trailing-edge-margin-bottom, revert-layer),
var(--ion-item-divider-icon-trailing-edge-margin-start, revert-layer)
);
}
// Note
::slotted(ion-note) {
@include mixins.margin(
var(--ion-item-divider-note-margin-top),
var(--ion-item-divider-note-margin-end),
var(--ion-item-divider-note-margin-bottom),
var(--ion-item-divider-note-margin-start)
);
align-self: flex-start;
font-size: var(--ion-item-divider-note-font-size, revert-layer);
}
::slotted(ion-note[slot]) {
@include mixins.padding(
var(--ion-item-divider-note-padding-top),
var(--ion-item-divider-note-padding-end),
var(--ion-item-divider-note-padding-bottom),
var(--ion-item-divider-note-padding-start)
);
}
// Avatar
::slotted(ion-avatar) {
@include mixins.margin(
var(--ion-item-divider-avatar-margin-top),
var(--ion-item-divider-avatar-margin-end),
var(--ion-item-divider-avatar-margin-bottom),
var(--ion-item-divider-avatar-margin-start)
);
width: var(--ion-item-divider-avatar-width, revert-layer);
height: var(--ion-item-divider-avatar-height, revert-layer);
}
::slotted(ion-avatar[slot="start"]) {
@include mixins.margin-horizontal(
var(--ion-item-divider-avatar-leading-edge-margin-start),
var(--ion-item-divider-avatar-leading-edge-margin-end)
);
}
::slotted(ion-avatar[slot="end"]) {
@include mixins.margin-horizontal(
var(--ion-item-divider-avatar-trailing-edge-margin-start),
var(--ion-item-divider-avatar-trailing-edge-margin-end)
);
}
// Thumbnail
::slotted(ion-thumbnail) {
// TODO(FW-6862): separate width and height tokens for thumbnails
--size: var(--ion-item-divider-thumbnail-width);
@include mixins.margin(
var(--ion-item-divider-thumbnail-margin-top),
var(--ion-item-divider-thumbnail-margin-end),
var(--ion-item-divider-thumbnail-margin-bottom),
var(--ion-item-divider-thumbnail-margin-start)
);
}
::slotted(ion-thumbnail[slot="start"]) {
@include mixins.margin-horizontal(
var(--ion-item-divider-thumbnail-leading-edge-margin-start),
var(--ion-item-divider-thumbnail-leading-edge-margin-end)
);
}
::slotted(ion-thumbnail[slot="end"]) {
@include mixins.margin-horizontal(
var(--ion-item-divider-thumbnail-trailing-edge-margin-start),
var(--ion-item-divider-thumbnail-trailing-edge-margin-end)
);
}
// Headers
::slotted(h1) {
@include mixins.margin(
var(--ion-item-divider-header1-margin-top),
var(--ion-item-divider-header1-margin-end),
var(--ion-item-divider-header1-margin-bottom),
var(--ion-item-divider-header1-margin-start)
);
}
::slotted(h2) {
@include mixins.margin(
var(--ion-item-divider-header2-margin-top),
var(--ion-item-divider-header2-margin-end),
var(--ion-item-divider-header2-margin-bottom),
var(--ion-item-divider-header2-margin-start)
);
}
::slotted(h2:last-child) {
@include mixins.margin(
var(--ion-item-divider-header2-trailing-margin-top),
var(--ion-item-divider-header2-trailing-margin-end),
var(--ion-item-divider-header2-trailing-margin-bottom),
var(--ion-item-divider-header2-trailing-margin-start)
);
}
::slotted(h3) {
@include mixins.margin(
var(--ion-item-divider-header3-margin-top),
var(--ion-item-divider-header3-margin-end),
var(--ion-item-divider-header3-margin-bottom),
var(--ion-item-divider-header3-margin-start)
);
}
::slotted(h3:last-child) {
@include mixins.margin(
var(--ion-item-divider-header3-trailing-margin-top),
var(--ion-item-divider-header3-trailing-margin-end),
var(--ion-item-divider-header3-trailing-margin-bottom),
var(--ion-item-divider-header3-trailing-margin-start)
);
}
::slotted(h4) {
@include mixins.margin(
var(--ion-item-divider-header4-margin-top),
var(--ion-item-divider-header4-margin-end),
var(--ion-item-divider-header4-margin-bottom),
var(--ion-item-divider-header4-margin-start)
);
}
::slotted(h4:last-child) {
@include mixins.margin(
var(--ion-item-divider-header4-trailing-margin-top),
var(--ion-item-divider-header4-trailing-margin-end),
var(--ion-item-divider-header4-trailing-margin-bottom),
var(--ion-item-divider-header4-trailing-margin-start)
);
}
::slotted(h5) {
@include mixins.margin(
var(--ion-item-divider-header5-margin-top),
var(--ion-item-divider-header5-margin-end),
var(--ion-item-divider-header5-margin-bottom),
var(--ion-item-divider-header5-margin-start)
);
}
::slotted(h5:last-child) {
@include mixins.margin(
var(--ion-item-divider-header5-trailing-margin-top),
var(--ion-item-divider-header5-trailing-margin-end),
var(--ion-item-divider-header5-trailing-margin-bottom),
var(--ion-item-divider-header5-trailing-margin-start)
);
}
::slotted(h6) {
@include mixins.margin(
var(--ion-item-divider-header6-margin-top),
var(--ion-item-divider-header6-margin-end),
var(--ion-item-divider-header6-margin-bottom),
var(--ion-item-divider-header6-margin-start)
);
}
::slotted(h6:last-child) {
@include mixins.margin(
var(--ion-item-divider-header6-trailing-margin-top),
var(--ion-item-divider-header6-trailing-margin-end),
var(--ion-item-divider-header6-trailing-margin-bottom),
var(--ion-item-divider-header6-trailing-margin-start)
);
}
// Paragraph
::slotted(p) {
@include mixins.margin(
var(--ion-item-divider-paragraph-margin-top),
var(--ion-item-divider-paragraph-margin-end),
var(--ion-item-divider-paragraph-margin-bottom),
var(--ion-item-divider-paragraph-margin-start)
);
color: var(--ion-item-divider-paragraph-color);
font-size: var(--ion-item-divider-paragraph-font-size);
text-overflow: var(--ion-item-divider-paragraph-text-overflow);
overflow: var(--ion-item-divider-paragraph-overflow); // native are the same, can ionic be the same?
}
::slotted(p:last-child) {
@include mixins.margin(
var(--ion-item-divider-paragraph-trailing-margin-top),
var(--ion-item-divider-paragraph-trailing-margin-end),
var(--ion-item-divider-paragraph-trailing-margin-bottom, 0),
var(--ion-item-divider-paragraph-trailing-margin-start)
);
}

View File

@@ -2,12 +2,10 @@ import type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Prop, h } from '@stencil/core';
import { createColorClasses } from '@utils/theme';
import { getIonTheme } from '../../global/ionic-global';
import type { Color } from '../../interface';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component.
* @virtualProp {"ios" | "md" | "ionic"} theme - The theme determines the visual appearance of the component.
*
* @slot - Content is placed between the named slots if provided without a slot.
* @slot start - Content is placed to the left of the divider text in LTR, and to the right in RTL.
@@ -15,11 +13,7 @@ import type { Color } from '../../interface';
*/
@Component({
tag: 'ion-item-divider',
styleUrls: {
ios: 'item-divider.ios.scss',
md: 'item-divider.md.scss',
ionic: 'item-divider.md.scss',
},
styleUrl: 'item-divider.scss',
shadow: true,
})
export class ItemDivider implements ComponentInterface {
@@ -42,12 +36,12 @@ export class ItemDivider implements ComponentInterface {
@Prop() sticky = false;
render() {
const theme = getIonTheme(this);
const { color, sticky } = this;
return (
<Host
class={createColorClasses(this.color, {
[theme]: true,
'item-divider-sticky': this.sticky,
class={createColorClasses(color, {
'item-divider-sticky': sticky,
item: true,
})}
>

View File

@@ -71,6 +71,12 @@
</ion-item-group>
<ion-item-group>
<ion-item-divider>
<ion-icon slot="start" name="sunny"></ion-icon>
<ion-icon slot="end" name="partly-sunny"></ion-icon>
<ion-label>Light</ion-label>
</ion-item-divider>
<ion-item-divider color="dark">
<ion-button slot="start" fill="clear">
<ion-icon slot="icon-only" name="cloudy"></ion-icon>
@@ -103,9 +109,15 @@
<ion-button slot="end" fill="clear" color="danger">
<ion-icon name="sunny"></ion-icon>
</ion-button>
<ion-avatar slot="end">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==" />
</ion-avatar>
</ion-item-divider>
<ion-item-divider color="primary">
<ion-thumbnail slot="start">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==" />
</ion-thumbnail>
<ion-label>Primary</ion-label>
<ion-thumbnail slot="end">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==" />
@@ -122,6 +134,13 @@
<ion-label>Column 3</ion-label>
</ion-item>
</ion-item-group>
<ion-item-group>
<ion-item-divider>
<p>Paragraph</p>
<p>Paragraph Last Child</p>
</ion-item-divider>
</ion-item-group>
</ion-content>
<style>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 55 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -8,15 +8,15 @@
* @prop --border-radius: Border radius of the thumbnail
* @prop --size: Size of the thumbnail
*/
--size: 48px;
--size: 48px; // TODO(FW-6862): separate width and height tokens for thumbnails
--border-radius: 0;
@include border-radius(var(--border-radius));
display: block;
width: var(--size);
height: var(--size);
width: var(--size, 48px);
height: var(--size, 48px);
}
::slotted(ion-img),

View File

@@ -0,0 +1,23 @@
/**
* Common tokens utilized multiple times across all theme files.
*/
const hexColors = {
white: '#ffffff',
black: '#000000',
};
const rgbColors = {
white: '255, 255, 255',
black: '0, 0, 0',
};
export const colors = {
...hexColors,
backgroundColor: `var(--ion-background-color, ${hexColors.white})`,
backgroundColorRgb: `var(--ion-background-color-rgb, ${rgbColors.white})`,
textColor: `var(--ion-text-color, ${hexColors.black})`,
textColorRgb: `var(--ion-text-color-rgb, ${rgbColors.black})`,
};

View File

@@ -1,9 +1,11 @@
import { currentColor } from '../../utils/theme';
import { currentColor, mix, dynamicFont } from '../../utils/theme';
import { defaultTheme as baseDefaultTheme } from '../base/default.tokens';
import { colors as baseColors } from '../base/shared.tokens';
import type { DefaultTheme } from '../themes.interfaces';
import { darkTheme } from './dark.tokens';
import { lightTheme } from './light.tokens';
import { components } from './shared.tokens';
export const defaultTheme: DefaultTheme = {
...baseDefaultTheme,
@@ -261,5 +263,278 @@ export const defaultTheme: DefaultTheme = {
},
},
},
IonItemDivider: {
background: baseColors.backgroundColor,
color: `var(--ion-text-color-step-600, ${mix(baseColors.white, baseColors.black, '40%')})`,
minHeight: 'var(--ion-scaling-750)',
zIndex: 100,
padding: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-0)',
start: components.item.padding.start,
},
inner: {
padding: {
top: 'var(--ion-spacing-0)',
end: components.item.inner.padding.end,
bottom: 'var(--ion-spacing-0)',
start: 'var(--ion-spacing-0)',
},
},
border: {
bottom: `1px solid ${components.item.border.color}`,
},
font: {
size: dynamicFont(components.itemDivider.font.size),
},
leading: {
// Targets `::slotted([slot="start"])`
edge: {
margin: {
end: components.item.start.slot.margin.end,
},
},
},
trailing: {
// Targets `::slotted([slot="end"])`
edge: {
margin: {
start: components.item.end.slot.margin.start,
},
},
},
// Targets `ion-label`
label: {
margin: {
top: '13px',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-250)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `ion-icon`
icon: {
font: {
/**
* The icon's font size should use em units to support
* font scaling but evaluate to 24px at 100% font size.
* The value in em units is calculated by dividing
* the icon's font size in pixels by the item divider's
* font size in pixels.
* e.g. 24px / 14px = 1.7142857143em
*/
size: `${components.item.icon.slot.font.size / components.itemDivider.font.size}em`,
},
leading: {
// Targets `ion-icon[slot="start"]`
edge: {
margin: {
top: components.item.icon.slot.margin.top,
end: components.item.icon.start.slot.margin.end,
bottom: components.item.icon.slot.margin.bottom,
},
},
},
trailing: {
// Targets `ion-icon[slot="end"]`
edge: {
margin: {
top: components.item.icon.slot.margin.top,
start: components.item.icon.end.slot.margin.start,
bottom: components.item.icon.slot.margin.bottom,
},
},
},
// Default non-semantic states
default: {
color: components.item.icon.slot.color,
},
// Any of the semantic colors like primary, secondary, etc.
semantic: {
default: {
color: currentColor('contrast'),
},
},
},
// Targets `ion-note`
note: {
align: {
self: 'flex-start',
},
font: {
size: dynamicFont(components.item.note.slot.font.size),
},
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-0)',
start: 'var(--ion-spacing-0)',
},
padding: {
top: components.item.note.slot.padding.top,
end: components.item.note.slot.padding.end,
bottom: components.item.note.slot.padding.bottom,
start: components.item.note.slot.padding.start,
},
},
// Targets `ion-avatar`
avatar: {
height: components.item.avatar.height,
width: components.item.avatar.width,
margin: {
top: components.item.media.slot.margin.top,
bottom: components.item.media.slot.margin.bottom,
},
leading: {
// Targets `ion-avatar[slot="start"]`
edge: {
margin: {
end: components.item.media.start.slot.margin.end,
},
},
},
trailing: {
// Targets `ion-avatar[slot="end"]`
edge: {
margin: {
start: components.item.media.end.slot.margin.start,
},
},
},
},
// Targets `ion-thumbnail`
thumbnail: {
// TODO: switch from size to height and width
height: components.item.thumbnail.height,
width: components.item.thumbnail.width,
margin: {
top: components.item.media.slot.margin.top,
bottom: components.item.media.slot.margin.bottom,
},
leading: {
// Targets `ion-thumbnail[slot="start"]`
edge: {
margin: {
end: components.item.media.start.slot.margin.end,
},
},
},
trailing: {
// Targets `ion-thumbnail[slot="end"]`
edge: {
margin: {
start: components.item.media.end.slot.margin.start,
},
},
},
},
// Targets `h1`
header1: {
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `h2`
header2: {
margin: {
top: 'var(--ion-spacing-xxxs)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `h3`
header3: {
margin: {
top: 'var(--ion-spacing-xxxs)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `h4`
header4: {
margin: {
top: 'var(--ion-spacing-xxxs)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `h5`
header5: {
margin: {
top: 'var(--ion-spacing-xxxs)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `h6`
header6: {
margin: {
top: 'var(--ion-spacing-xxxs)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `p`
paragraph: {
color: components.item.paragraph.color,
overflow: 'inherit',
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
font: {
size: dynamicFont(14),
},
text: {
overflow: 'initial',
},
},
},
},
};

View File

@@ -0,0 +1,137 @@
/**
* Common tokens utilized multiple times across `ionic` theme files.
*/
import { mix, rgba } from '../../utils/theme';
import { colors as baseColors } from '../base/shared.tokens';
export const components = {
item: {
padding: {
start: 'var(--ion-spacing-lg)',
},
inner: {
padding: {
end: 'var(--ion-spacing-lg)',
},
},
border: {
color: `var(--ion-item-border-color, var(--ion-border-color, var(--ion-background-color-step-150, ${rgba(
'0, 0, 0',
0.13
)})))`,
},
// ::slotted([slot="start"])
start: {
slot: {
margin: {
end: 'var(--ion-spacing-lg)',
},
},
},
// ::slotted([slot="end"])
end: {
slot: {
margin: {
start: 'var(--ion-spacing-lg)',
},
},
},
icon: {
slot: {
color: rgba(baseColors.textColorRgb, 0.54),
font: {
size: 24,
},
margin: {
top: 'var(--ion-spacing-md)',
bottom: 'var(--ion-spacing-md)',
},
},
start: {
slot: {
margin: {
end: 'var(--ion-spacing-xxxxl)',
},
},
},
end: {
slot: {
margin: {
start: 'var(--ion-spacing-lg)',
},
},
},
},
note: {
slot: {
font: {
size: 11,
},
padding: {
top: 'var(--ion-spacing-450)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-250)',
start: 'var(--ion-spacing-0)',
},
},
},
avatar: {
width: 'var(--ion-scaling-xl)',
height: 'var(--ion-scaling-xl)',
},
thumbnail: {
width: 'var(--ion-scaling-1400)',
height: 'var(--ion-scaling-1400)',
},
// media: avatar and thumbnail
media: {
slot: {
margin: {
top: 'var(--ion-spacing-sm)',
bottom: 'var(--ion-spacing-sm)',
},
},
start: {
slot: {
margin: {
end: 'var(--ion-spacing-lg)',
},
},
},
end: {
slot: {
margin: {
start: 'var(--ion-spacing-lg)',
},
},
},
},
paragraph: {
color: `var(--ion-text-color-400, ${mix(baseColors.white, baseColors.black, '40%')})`,
},
},
itemDivider: {
font: {
size: 14,
},
},
};

View File

@@ -1,16 +1,13 @@
import { rgba, currentColor, clamp } from '../../utils/theme';
import { rgba, currentColor, clamp, mix, dynamicFont } from '../../utils/theme';
import { defaultTheme as baseDefaultTheme } from '../base/default.tokens';
import { colors as baseColors } from '../base/shared.tokens';
import type { DefaultTheme } from '../themes.interfaces';
import { darkTheme } from './dark.tokens';
import { highContrastDarkTheme } from './high-contrast-dark.tokens';
import { highContrastTheme } from './high-contrast.tokens';
import { lightTheme } from './light.tokens';
const fontSizes = {
chipBase: 14,
root: parseFloat(baseDefaultTheme.fontSize!.root as string),
};
import { global, components } from './shared.tokens';
export const defaultTheme: DefaultTheme = {
...baseDefaultTheme,
@@ -117,7 +114,7 @@ export const defaultTheme: DefaultTheme = {
font: {
size: clamp(
'var(--ion-font-size-xs)',
`${((fontSizes.chipBase - 2) / fontSizes.root).toFixed(2)}rem`,
`${((components.chip.font.size - 2) / global.root).toFixed(2)}rem`,
'var(--ion-font-size-xl)'
),
},
@@ -127,7 +124,7 @@ export const defaultTheme: DefaultTheme = {
minHeight: 'var(--ion-scaling-md)',
font: {
size: clamp('13px', `${(fontSizes.chipBase / fontSizes.root).toFixed(2)}rem`, '22px'),
size: clamp('13px', `${(components.chip.font.size / global.root).toFixed(2)}rem`, '22px'),
},
},
},
@@ -355,9 +352,10 @@ export const defaultTheme: DefaultTheme = {
color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.54),
font: {
size: `${(20 / fontSizes.chipBase).toFixed(2)}em`,
size: `${(20 / components.chip.font.size).toFixed(2)}em`,
},
// Targets `:first-child`
leading: {
margin: {
top: '-4px',
@@ -367,6 +365,7 @@ export const defaultTheme: DefaultTheme = {
},
},
// Targets `:last-child`
trailing: {
margin: {
top: '-4px',
@@ -378,9 +377,10 @@ export const defaultTheme: DefaultTheme = {
},
avatar: {
height: `${(24 / fontSizes.chipBase).toFixed(2)}em`,
width: `${(24 / fontSizes.chipBase).toFixed(2)}em`,
height: `${(24 / components.chip.font.size).toFixed(2)}em`,
width: `${(24 / components.chip.font.size).toFixed(2)}em`,
// Targets `:first-child`
leading: {
margin: {
top: '-4px',
@@ -390,6 +390,7 @@ export const defaultTheme: DefaultTheme = {
},
},
// Targets `:last-child`
trailing: {
margin: {
top: '-4px',
@@ -400,5 +401,179 @@ export const defaultTheme: DefaultTheme = {
},
},
},
IonItemDivider: {
background: `var(--ion-background-color-step-100, ${mix(baseColors.black, baseColors.white, '90%')})`,
color: `var(--ion-text-color-step-150, ${mix(baseColors.white, baseColors.black, '85%')})`,
minHeight: 'var(--ion-scaling-sm)',
padding: {
start: components.item.padding.start,
},
inner: {
padding: {
end: `calc(${components.item.padding.end} * 0.5)`,
},
},
font: {
size: dynamicFont(17),
weight: 'var(--ion-font-weight-semi-bold)',
},
leading: {
// Targets `:host([slot="start"])`
anchor: {
margin: {
top: components.item.slot.start.margin.top,
end: components.item.slot.start.margin.end,
bottom: components.item.slot.start.margin.bottom,
start: components.item.slot.start.margin.start,
},
},
},
// Targets `ion-icon`
icon: {
leading: {
// Targets `ion-icon[slot="start"]`
edge: {
margin: {
top: components.item.icon.slot.margin.top,
bottom: components.item.icon.slot.margin.bottom,
},
},
},
trailing: {
// Targets `ion-icon[slot="end"]`
edge: {
margin: {
top: components.item.icon.slot.margin.top,
bottom: components.item.icon.slot.margin.bottom,
},
},
},
},
// Targets `h1`
header1: {
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `h2`
header2: {
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
// Targets `:last-child`
trailing: {
margin: {
bottom: 'var(--ion-spacing-0)',
},
},
},
// Targets `h3`
header3: {
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-75)',
start: 'var(--ion-spacing-0)',
},
trailing: {
margin: {
bottom: 'var(--ion-spacing-0)',
},
},
},
// Targets `h4`
header4: {
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-75)',
start: 'var(--ion-spacing-0)',
},
trailing: {
margin: {
bottom: 'var(--ion-spacing-0)',
},
},
},
// Targets `h5`
header5: {
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-75)',
start: 'var(--ion-spacing-0)',
},
trailing: {
margin: {
bottom: 'var(--ion-spacing-0)',
},
},
},
// Targets `h6`
header6: {
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-75)',
start: 'var(--ion-spacing-0)',
},
trailing: {
margin: {
bottom: 'var(--ion-spacing-0)',
},
},
},
// Targets `p`
paragraph: {
color: components.item.paragraph.color,
overflow: 'inherit',
margin: {
top: components.item.paragraph.margin.top,
end: components.item.paragraph.margin.end,
bottom: components.item.paragraph.margin.bottom,
start: components.item.paragraph.margin.start,
},
font: {
size: components.item.paragraph.font.size,
},
text: {
overflow: 'inherit',
},
trailing: {
margin: {
bottom: 'var(--ion-spacing-0)',
},
},
},
},
},
};

View File

@@ -0,0 +1,68 @@
/**
* Common tokens utilized multiple times across `ios` theme files.
*/
import { dynamicFont } from '../../utils/theme';
import { defaultTheme as baseDefaultTheme } from '../base/default.tokens';
export const global = {
root: parseFloat(baseDefaultTheme.fontSize!.root as string),
};
export const components = {
chip: {
font: {
size: 14,
},
},
item: {
padding: {
start: 'var(--ion-spacing-lg)',
end: 'var(--ion-spacing-lg)',
},
inner: {
padding: {
end: 'var(--ion-spacing-lg)',
},
},
// Targets `:host([slot="start"])`
slot: {
start: {
margin: {
top: 'var(--ion-spacing-xxxs)',
// Same as `padding-end`
end: 'var(--ion-spacing-lg)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
},
icon: {
slot: {
margin: {
top: '7px',
bottom: '7px',
},
},
},
paragraph: {
color: 'var(--ion-text-color-step-550, #a3a3a3)',
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
font: {
size: dynamicFont(14),
},
},
},
};

View File

@@ -1,16 +1,13 @@
import { rgba, currentColor } from '../../utils/theme';
import { rgba, currentColor, mix, dynamicFont } from '../../utils/theme';
import { defaultTheme as baseDefaultTheme } from '../base/default.tokens';
import { colors as baseColors } from '../base/shared.tokens';
import type { DefaultTheme } from '../themes.interfaces';
import { darkTheme } from './dark.tokens';
import { highContrastDarkTheme } from './high-contrast-dark.tokens';
import { highContrastTheme } from './high-contrast.tokens';
import { lightTheme } from './light.tokens';
const fontSizes = {
chipBase: 14,
root: parseFloat(baseDefaultTheme.fontSize!.root as string),
};
import { global, components } from './shared.tokens';
export const defaultTheme: DefaultTheme = {
...baseDefaultTheme,
@@ -118,14 +115,14 @@ export const defaultTheme: DefaultTheme = {
minHeight: 'var(--ion-scaling-xs)',
font: {
size: `${((fontSizes.chipBase - 2) / fontSizes.root).toFixed(2)}rem`,
size: `${((components.chip.font.size - 2) / global.root).toFixed(2)}rem`,
},
},
large: {
minHeight: 'var(--ion-scaling-md)',
font: {
size: `${(fontSizes.chipBase / fontSizes.root).toFixed(2)}rem`,
size: `${(components.chip.font.size / global.root).toFixed(2)}rem`,
},
},
},
@@ -348,11 +345,12 @@ export const defaultTheme: DefaultTheme = {
},
},
// Targets `ion-icon`
icon: {
color: rgba('var(--ion-text-color-rgb, 0, 0, 0)', 0.54),
font: {
size: `${(20 / fontSizes.chipBase).toFixed(2)}em`,
size: `${(20 / components.chip.font.size).toFixed(2)}em`,
},
leading: {
@@ -374,9 +372,10 @@ export const defaultTheme: DefaultTheme = {
},
},
// Targets `ion-avatar`
avatar: {
height: `${(24 / fontSizes.chipBase).toFixed(2)}em`,
width: `${(24 / fontSizes.chipBase).toFixed(2)}em`,
height: `${(24 / components.chip.font.size).toFixed(2)}em`,
width: `${(24 / components.chip.font.size).toFixed(2)}em`,
leading: {
margin: {
@@ -397,5 +396,278 @@ export const defaultTheme: DefaultTheme = {
},
},
},
IonItemDivider: {
background: baseColors.backgroundColor,
color: `var(--ion-text-color-step-600, ${mix(baseColors.white, baseColors.black, '40%')})`,
minHeight: 'var(--ion-scaling-750)',
zIndex: 100,
padding: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-0)',
start: components.item.padding.start,
},
inner: {
padding: {
top: 'var(--ion-spacing-0)',
end: components.item.inner.padding.end,
bottom: 'var(--ion-spacing-0)',
start: 'var(--ion-spacing-0)',
},
},
border: {
bottom: `1px solid ${components.item.border.color}`,
},
font: {
size: dynamicFont(components.itemDivider.font.size),
},
leading: {
// Targets `::slotted([slot="start"])`
edge: {
margin: {
end: components.item.start.slot.margin.end,
},
},
},
trailing: {
// Targets `::slotted([slot="end"])`
edge: {
margin: {
start: components.item.end.slot.margin.start,
},
},
},
// Targets `ion-label`
label: {
margin: {
top: '13px',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-250)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `ion-icon`
icon: {
font: {
/**
* The icon's font size should use em units to support
* font scaling but evaluate to 24px at 100% font size.
* The value in em units is calculated by dividing
* the icon's font size in pixels by the item divider's
* font size in pixels.
* e.g. 24px / 14px = 1.7142857143em
*/
size: `${components.item.icon.slot.font.size / components.itemDivider.font.size}em`,
},
leading: {
// Targets `ion-icon[slot="start"]`
edge: {
margin: {
top: components.item.icon.slot.margin.top,
end: components.item.icon.start.slot.margin.end,
bottom: components.item.icon.slot.margin.bottom,
},
},
},
trailing: {
// Targets `ion-icon[slot="end"]`
edge: {
margin: {
top: components.item.icon.slot.margin.top,
start: components.item.icon.end.slot.margin.start,
bottom: components.item.icon.slot.margin.bottom,
},
},
},
// Default non-semantic states
default: {
color: components.item.icon.slot.color,
},
// Any of the semantic colors like primary, secondary, etc.
semantic: {
default: {
color: currentColor('contrast'),
},
},
},
// Targets `ion-note`
note: {
align: {
self: 'flex-start',
},
font: {
size: dynamicFont(components.item.note.slot.font.size),
},
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-0)',
start: 'var(--ion-spacing-0)',
},
padding: {
top: components.item.note.slot.padding.top,
end: components.item.note.slot.padding.end,
bottom: components.item.note.slot.padding.bottom,
start: components.item.note.slot.padding.start,
},
},
// Targets `ion-avatar`
avatar: {
height: components.item.avatar.height,
width: components.item.avatar.width,
margin: {
top: components.item.media.slot.margin.top,
bottom: components.item.media.slot.margin.bottom,
},
leading: {
// Targets `ion-avatar[slot="start"]`
edge: {
margin: {
end: components.item.media.start.slot.margin.end,
},
},
},
trailing: {
// Targets `ion-avatar[slot="end"]`
edge: {
margin: {
start: components.item.media.end.slot.margin.start,
},
},
},
},
// Targets `ion-thumbnail`
thumbnail: {
// TODO: switch from size to height and width
height: components.item.thumbnail.height,
width: components.item.thumbnail.width,
margin: {
top: components.item.media.slot.margin.top,
bottom: components.item.media.slot.margin.bottom,
},
leading: {
// Targets `ion-thumbnail[slot="start"]`
edge: {
margin: {
end: components.item.media.start.slot.margin.end,
},
},
},
trailing: {
// Targets `ion-thumbnail[slot="end"]`
edge: {
margin: {
start: components.item.media.end.slot.margin.start,
},
},
},
},
// Targets `h1`
header1: {
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `h2`
header2: {
margin: {
top: 'var(--ion-spacing-xxxs)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `h3`
header3: {
margin: {
top: 'var(--ion-spacing-xxxs)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `h4`
header4: {
margin: {
top: 'var(--ion-spacing-xxxs)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `h5`
header5: {
margin: {
top: 'var(--ion-spacing-xxxs)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `h6`
header6: {
margin: {
top: 'var(--ion-spacing-xxxs)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
},
// Targets `p`
paragraph: {
color: components.item.paragraph.color,
overflow: 'inherit',
margin: {
top: 'var(--ion-spacing-0)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-xxxs)',
start: 'var(--ion-spacing-0)',
},
font: {
size: dynamicFont(14),
},
text: {
overflow: 'initial',
},
},
},
},
};

View File

@@ -0,0 +1,148 @@
/**
* Common tokens utilized multiple times across `md` theme files.
*/
import { mix, rgba } from '../../utils/theme';
import { defaultTheme as baseDefaultTheme } from '../base/default.tokens';
import { colors as baseColors } from '../base/shared.tokens';
export const global = {
root: parseFloat(baseDefaultTheme.fontSize!.root as string),
};
export const components = {
chip: {
font: {
size: 14,
},
},
item: {
padding: {
start: 'var(--ion-spacing-lg)',
},
inner: {
padding: {
end: 'var(--ion-spacing-lg)',
},
},
border: {
color: `var(--ion-item-border-color, var(--ion-border-color, var(--ion-background-color-step-150, ${rgba(
baseColors.textColorRgb,
0.13
)})))`,
},
// ::slotted([slot="start"])
start: {
slot: {
margin: {
end: 'var(--ion-spacing-lg)',
},
},
},
// ::slotted([slot="end"])
end: {
slot: {
margin: {
start: 'var(--ion-spacing-lg)',
},
},
},
icon: {
slot: {
color: rgba(baseColors.textColorRgb, 0.54),
font: {
size: 24,
},
margin: {
top: 'var(--ion-spacing-md)',
bottom: 'var(--ion-spacing-md)',
},
},
start: {
slot: {
margin: {
end: 'var(--ion-spacing-xxxxl)',
},
},
},
end: {
slot: {
margin: {
start: 'var(--ion-spacing-lg)',
},
},
},
},
note: {
slot: {
font: {
size: 11,
},
padding: {
top: 'var(--ion-spacing-450)',
end: 'var(--ion-spacing-0)',
bottom: 'var(--ion-spacing-250)',
start: 'var(--ion-spacing-0)',
},
},
},
avatar: {
width: 'var(--ion-scaling-xl)',
height: 'var(--ion-scaling-xl)',
},
thumbnail: {
width: 'var(--ion-scaling-1400)',
height: 'var(--ion-scaling-1400)',
},
// media: avatar and thumbnail
media: {
slot: {
margin: {
top: 'var(--ion-spacing-sm)',
bottom: 'var(--ion-spacing-sm)',
},
},
start: {
slot: {
margin: {
end: 'var(--ion-spacing-lg)',
},
},
},
end: {
slot: {
margin: {
start: 'var(--ion-spacing-lg)',
},
},
},
},
paragraph: {
color: `var(--ion-text-color-400, ${mix(baseColors.white, baseColors.black, '40%')})`,
},
},
itemDivider: {
font: {
size: 14,
},
},
};

View File

@@ -1,4 +1,5 @@
import type { IonChipRecipe, IonChipConfig } from '../components/chip/chip.interfaces';
import type { IonItemDividerRecipe } from '../components/item-divider/item-divider.interfaces';
import type { IonicConfig as IonicGlobalConfig } from '../utils/config';
// Platform-specific theme
@@ -282,6 +283,7 @@ export type DefaultTheme = BaseTheme & {
type Components = {
IonChip?: IonChipRecipe;
IonItemDivider?: IonItemDividerRecipe;
IonCard?: any;
IonItem?: any;

View File

@@ -633,3 +633,22 @@ export function currentColor(variation: string, alpha: number | string | null =
export function clamp(min: number | string, val: number | string, max: number | string): string {
return `clamp(${min}, ${val}, ${max})`;
}
const baselinePixelSize = 16;
const baselineUnit = 'rem';
/**
* Converts a pixel value to a dynamic unit (defaulting to rem).
*
* Examples:
* - `dynamicFont(16)` returns `'1.000rem'`
* - `dynamicFont(20, 'em')` returns `'1.250em'`
*
* @internal
* @param size - The numeric pixel value.
* @param unit - The CSS unit string.
* @returns The calculated CSS value string.
*/
export const dynamicFont = (size: number, unit: string = baselineUnit): string => {
return `${size / baselinePixelSize}${unit}`;
};

View File

@@ -1150,14 +1150,14 @@ export declare interface IonItem extends Components.IonItem {}
@ProxyCmp({
inputs: ['color', 'mode', 'sticky', 'theme']
inputs: ['color', 'mode', 'sticky']
})
@Component({
selector: 'ion-item-divider',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['color', 'mode', 'sticky', 'theme'],
inputs: ['color', 'mode', 'sticky'],
})
export class IonItemDivider {
protected el: HTMLIonItemDividerElement;

View File

@@ -1058,14 +1058,14 @@ export declare interface IonItem extends Components.IonItem {}
@ProxyCmp({
defineCustomElementFn: defineIonItemDivider,
inputs: ['color', 'mode', 'sticky', 'theme']
inputs: ['color', 'mode', 'sticky']
})
@Component({
selector: 'ion-item-divider',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['color', 'mode', 'sticky', 'theme'],
inputs: ['color', 'mode', 'sticky'],
standalone: true
})
export class IonItemDivider {