Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,7 @@ ion-fab-list,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-footer,none
ion-footer,prop,collapse,"fade" | undefined,undefined,false,false
ion-footer,prop,mode,"ios" | "md",undefined,false,false
ion-footer,prop,scrollEffect,"fade" | "hide" | undefined,undefined,false,false
ion-footer,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-footer,prop,translucent,boolean,false,false,false

Expand Down Expand Up @@ -937,6 +938,7 @@ ion-header,none
ion-header,prop,collapse,"condense" | "fade" | undefined,undefined,false,false
ion-header,prop,divider,boolean,false,false,false
ion-header,prop,mode,"ios" | "md",undefined,false,false
ion-header,prop,scrollEffect,"condense" | "fade" | "hide" | undefined,undefined,false,false
ion-header,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-header,prop,translucent,boolean,false,false,false

Expand Down Expand Up @@ -2445,8 +2447,8 @@ ion-tab,method,setActive,setActive() => Promise<void>
ion-tab-bar,shadow
ion-tab-bar,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-tab-bar,prop,expand,"compact" | "full",'full',false,false
ion-tab-bar,prop,hideOnScroll,boolean,false,false,false
ion-tab-bar,prop,mode,"ios" | "md",undefined,false,false
ion-tab-bar,prop,scrollEffect,"hide" | undefined,undefined,false,false
ion-tab-bar,prop,selectedTab,string | undefined,undefined,false,false
ion-tab-bar,prop,shape,"rectangular" | "round" | "soft" | undefined,undefined,false,false
ion-tab-bar,prop,theme,"ios" | "md" | "ionic",undefined,false,false
Expand Down
50 changes: 37 additions & 13 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import { BreadcrumbCollapsedClickEventDetail } from "./components/breadcrumb/bre
import { CheckboxChangeEventDetail } from "./components/checkbox/checkbox-interface";
import { ScrollBaseDetail, ScrollDetail } from "./components/content/content-interface";
import { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimeParts, DatetimePresentation, FormatOptions, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface";
import { FooterScrollEffect } from "./components/footer/footer-interface";
import { GalleryColumns, GalleryGap } from "./components/gallery/gallery-interface";
import { HeaderScrollEffect } from "./components/header/header-interface";
import { SpinnerTypes } from "./components/spinner/spinner-configs";
import { InputChangeEventDetail, InputInputEventDetail } from "./components/input/input-interface";
import { InputOtpChangeEventDetail, InputOtpCompleteEventDetail, InputOtpInputEventDetail } from "./components/input-otp/input-otp-interface";
Expand All @@ -40,7 +42,7 @@ import { SegmentViewScrollEvent } from "./components/segment-view/segment-view-i
import { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./components/select/select-interface";
import { SelectModalOption } from "./components/select-modal/select-modal-interface";
import { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
import { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
import { TabBarChangedEventDetail, TabBarScrollEffect, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
import { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
import { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions, ToastSwipeGestureDirection } from "./components/toast/toast-interface";
import { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
Expand All @@ -55,7 +57,9 @@ export { BreadcrumbCollapsedClickEventDetail } from "./components/breadcrumb/bre
export { CheckboxChangeEventDetail } from "./components/checkbox/checkbox-interface";
export { ScrollBaseDetail, ScrollDetail } from "./components/content/content-interface";
export { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimeParts, DatetimePresentation, FormatOptions, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface";
export { FooterScrollEffect } from "./components/footer/footer-interface";
export { GalleryColumns, GalleryGap } from "./components/gallery/gallery-interface";
export { HeaderScrollEffect } from "./components/header/header-interface";
export { SpinnerTypes } from "./components/spinner/spinner-configs";
export { InputChangeEventDetail, InputInputEventDetail } from "./components/input/input-interface";
export { InputOtpChangeEventDetail, InputOtpCompleteEventDetail, InputOtpInputEventDetail } from "./components/input-otp/input-otp-interface";
Expand All @@ -79,7 +83,7 @@ export { SegmentViewScrollEvent } from "./components/segment-view/segment-view-i
export { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./components/select/select-interface";
export { SelectModalOption } from "./components/select-modal/select-modal-interface";
export { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
export { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
export { TabBarChangedEventDetail, TabBarScrollEffect, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
export { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
export { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions, ToastSwipeGestureDirection } from "./components/toast/toast-interface";
export { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
Expand Down Expand Up @@ -1459,12 +1463,17 @@ export namespace Components {
interface IonFooter {
/**
* Describes the scroll effect that will be applied to the footer. Only applies when the theme is `"ios"`.
* @deprecated Use `scrollEffect` instead.
*/
"collapse"?: 'fade';
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* Describes the scroll effect that will be applied to the footer. `"hide"` slides the footer out of view when scrolling down and back in when scrolling up. `"fade"` fades the toolbar background on scroll.
*/
"scrollEffect"?: FooterScrollEffect;
/**
* The theme determines the visual appearance of the component.
*/
Expand Down Expand Up @@ -1536,6 +1545,7 @@ export namespace Components {
interface IonHeader {
/**
* Describes the scroll effect that will be applied to the header. Only applies when the theme is `"ios"`. Typically used for [Collapsible Large Titles](https://ionicframework.com/docs/api/title#collapsible-large-titles)
* @deprecated Use `scrollEffect` instead.
*/
"collapse"?: 'condense' | 'fade';
/**
Expand All @@ -1547,6 +1557,10 @@ export namespace Components {
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* Describes the scroll effect that will be applied to the header. `"hide"` slides the header out of view when scrolling down and back in when scrolling up. `"condense"` collapses the large title into the main toolbar on scroll. `"fade"` fades the toolbar background on scroll.
*/
"scrollEffect"?: HeaderScrollEffect;
/**
* The theme determines the visual appearance of the component.
*/
Expand Down Expand Up @@ -4041,15 +4055,14 @@ export namespace Components {
* @default 'full'
*/
"expand": 'compact' | 'full';
/**
* If `true`, the tab bar will be hidden when the user scrolls down and shown when the user scrolls up. Only applies when the theme is `"ionic"` and `expand` is `"compact"`.
* @default false
*/
"hideOnScroll": boolean;
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* Describes the scroll effect that will be applied to the tab bar. `"hide"` slides the tab bar out of view when scrolling down and back in when scrolling up.
*/
"scrollEffect"?: TabBarScrollEffect;
/**
* The selected tab component
*/
Expand Down Expand Up @@ -7536,12 +7549,17 @@ declare namespace LocalJSX {
interface IonFooter {
/**
* Describes the scroll effect that will be applied to the footer. Only applies when the theme is `"ios"`.
* @deprecated Use `scrollEffect` instead.
*/
"collapse"?: 'fade';
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* Describes the scroll effect that will be applied to the footer. `"hide"` slides the footer out of view when scrolling down and back in when scrolling up. `"fade"` fades the toolbar background on scroll.
*/
"scrollEffect"?: FooterScrollEffect;
/**
* The theme determines the visual appearance of the component.
*/
Expand Down Expand Up @@ -7609,6 +7627,7 @@ declare namespace LocalJSX {
interface IonHeader {
/**
* Describes the scroll effect that will be applied to the header. Only applies when the theme is `"ios"`. Typically used for [Collapsible Large Titles](https://ionicframework.com/docs/api/title#collapsible-large-titles)
* @deprecated Use `scrollEffect` instead.
*/
"collapse"?: 'condense' | 'fade';
/**
Expand All @@ -7620,6 +7639,10 @@ declare namespace LocalJSX {
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* Describes the scroll effect that will be applied to the header. `"hide"` slides the header out of view when scrolling down and back in when scrolling up. `"condense"` collapses the large title into the main toolbar on scroll. `"fade"` fades the toolbar background on scroll.
*/
"scrollEffect"?: HeaderScrollEffect;
/**
* The theme determines the visual appearance of the component.
*/
Expand Down Expand Up @@ -10208,17 +10231,16 @@ declare namespace LocalJSX {
* @default 'full'
*/
"expand"?: 'compact' | 'full';
/**
* If `true`, the tab bar will be hidden when the user scrolls down and shown when the user scrolls up. Only applies when the theme is `"ionic"` and `expand` is `"compact"`.
* @default false
*/
"hideOnScroll"?: boolean;
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
"onIonTabBarChanged"?: (event: IonTabBarCustomEvent<TabBarChangedEventDetail>) => void;
"onIonTabBarLoaded"?: (event: IonTabBarCustomEvent<void>) => void;
/**
* Describes the scroll effect that will be applied to the tab bar. `"hide"` slides the tab bar out of view when scrolling down and back in when scrolling up.
*/
"scrollEffect"?: TabBarScrollEffect;
/**
* The selected tab component
*/
Expand Down Expand Up @@ -11015,6 +11037,7 @@ declare namespace LocalJSX {
"side": 'start' | 'end' | 'top' | 'bottom';
}
interface IonFooterAttributes {
"scrollEffect": FooterScrollEffect;
"collapse": 'fade';
"translucent": boolean;
}
Expand All @@ -11028,6 +11051,7 @@ declare namespace LocalJSX {
"fixed": boolean;
}
interface IonHeaderAttributes {
"scrollEffect": HeaderScrollEffect;
"collapse": 'condense' | 'fade';
"divider": boolean;
"translucent": boolean;
Expand Down Expand Up @@ -11465,7 +11489,7 @@ declare namespace LocalJSX {
interface IonTabBarAttributes {
"color": Color;
"selectedTab": string;
"hideOnScroll": boolean;
"scrollEffect": TabBarScrollEffect;
"translucent": boolean;
"expand": 'compact' | 'full';
"shape": 'soft' | 'round' | 'rectangular';
Expand Down
55 changes: 55 additions & 0 deletions core/src/components/content/content.scss
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,58 @@
*/
transform: translateZ(0);
}

// Content: Header/Footer/Tab Bar Scroll Effect Hide — Gap Compensation
// --------------------------------------------------
// When a bar hides via scrollEffect="hide", it stays in the layout flow
// but is translated out of view. The inner-scroll must shift and expand
// to fill the gap so no empty space is visible to the user.

// Header gap compensation — only when content is NOT fullscreen.
// When fullscreen=true, inner-scroll already fills the full viewport
// (offset-top = 0), so translateY would over-shift the content.
:host(.content-header-hide-scroll-partner:not(.content-fullscreen)) .inner-scroll {
transition: transform 300ms cubic-bezier(0, 0, 0.2, 1), height 300ms cubic-bezier(0, 0, 0.2, 1);
}

:host(.content-header-hide-scroll-partner:not(.content-fullscreen).content-header-hide-scroll-hidden) .inner-scroll {
height: calc(100% + var(--internal-header-hide-height, 0px));

transform: translateY(calc(-1 * var(--internal-header-hide-height, 0px)));

transition: transform 200ms cubic-bezier(0.4, 0, 1, 1), height 200ms cubic-bezier(0.4, 0, 1, 1);
}

// Footer gap compensation — only when content is NOT fullscreen
:host(.content-footer-hide-scroll-partner:not(.content-fullscreen)) .inner-scroll {
transition: height 200ms cubic-bezier(0.16, 1, 0.3, 1);
}

:host(.content-footer-hide-scroll-partner:not(.content-fullscreen).content-footer-hide-scroll-hidden) .inner-scroll {
height: calc(100% + var(--internal-footer-hide-height, 0px));

transition: height 350ms cubic-bezier(0.4, 0, 1, 1);
}

// Tab bar gap compensation — only when content is NOT fullscreen
:host(.content-tab-bar-hide-scroll-partner:not(.content-fullscreen)) .inner-scroll {
transition: height 200ms cubic-bezier(0.16, 1, 0.3, 1);
}

:host(.content-tab-bar-hide-scroll-partner:not(.content-fullscreen).content-tab-bar-hide-scroll-hidden) .inner-scroll {
height: calc(100% + var(--internal-tab-bar-hide-height, 0px));

transition: height 350ms cubic-bezier(0.4, 0, 1, 1);
}

@media (prefers-reduced-motion: reduce) {
:host(.content-header-hide-scroll-partner:not(.content-fullscreen)) .inner-scroll,
:host(.content-header-hide-scroll-partner:not(.content-fullscreen).content-header-hide-scroll-hidden) .inner-scroll,
:host(.content-footer-hide-scroll-partner:not(.content-fullscreen)) .inner-scroll,
:host(.content-footer-hide-scroll-partner:not(.content-fullscreen).content-footer-hide-scroll-hidden) .inner-scroll,
:host(.content-tab-bar-hide-scroll-partner:not(.content-fullscreen)) .inner-scroll,
:host(.content-tab-bar-hide-scroll-partner:not(.content-fullscreen).content-tab-bar-hide-scroll-hidden)
.inner-scroll {
transition: none;
}
}
1 change: 1 addition & 0 deletions core/src/components/footer/footer-interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type FooterScrollEffect = 'hide' | 'fade';
24 changes: 24 additions & 0 deletions core/src/components/footer/footer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,27 @@ ion-footer {
ion-footer.footer-toolbar-padding ion-toolbar:last-of-type {
padding-bottom: var(--ion-safe-area-bottom, 0);
}

// Footer - Scroll Effect Hide
// --------------------------------------------------

// Slide back into view when scrolling up — spring ease-out snaps the footer
// back into place quickly and settles smoothly.
ion-footer.footer-scroll-effect-hide {
transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1);
}

// Slide out of view when scrolling down — footer moves off the bottom of the
// screen. Slower duration mirrors the natural pace of the downward scroll.
ion-footer.footer-scroll-effect-hide.footer-scroll-hidden {
transform: translateY(100%);

transition: transform 350ms cubic-bezier(0.4, 0, 1, 1);
}

@media (prefers-reduced-motion: reduce) {
ion-footer.footer-scroll-effect-hide,
ion-footer.footer-scroll-effect-hide.footer-scroll-hidden {
transition: none;
}
}
Loading
Loading