@propbinder/mobile-design 0.4.41 → 0.4.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -63
- package/assets/fonts/brockmann-medium-webfont.woff2 +0 -0
- package/assets/fonts/brockmann-mediumitalic-webfont.woff2 +0 -0
- package/assets/fonts/brockmann-regular-webfont.woff2 +0 -0
- package/assets/fonts/brockmann-regularitalic-webfont.woff2 +0 -0
- package/assets/fonts/brockmann-semibold-webfont.woff2 +0 -0
- package/assets/fonts/brockmann-semibolditalic-webfont.woff2 +0 -0
- package/fesm2022/{propbinder-mobile-design-ds-mobile-days-sheet-BsahIVaB.mjs → propbinder-mobile-design-ds-mobile-days-sheet-Bf7hxhB6.mjs} +51 -51
- package/fesm2022/propbinder-mobile-design-ds-mobile-days-sheet-Bf7hxhB6.mjs.map +1 -0
- package/fesm2022/{propbinder-mobile-design-ds-mobile-duration-sheet-Dun-o-dR.mjs → propbinder-mobile-design-ds-mobile-duration-sheet-BHTlCfU7.mjs} +173 -173
- package/fesm2022/propbinder-mobile-design-ds-mobile-duration-sheet-BHTlCfU7.mjs.map +1 -0
- package/fesm2022/{propbinder-mobile-design-ds-mobile-time-sheet-B9Ny4wM7.mjs → propbinder-mobile-design-ds-mobile-time-sheet-CrUB7zl-.mjs} +69 -69
- package/fesm2022/propbinder-mobile-design-ds-mobile-time-sheet-CrUB7zl-.mjs.map +1 -0
- package/fesm2022/propbinder-mobile-design.mjs +15073 -15061
- package/fesm2022/propbinder-mobile-design.mjs.map +1 -1
- package/index.d.ts +11 -4
- package/package.json +1 -1
- package/styles/ionic.css +989 -989
- package/styles/mobile-common.css +155 -155
- package/styles/mobile-page-base.css +338 -338
- package/fesm2022/propbinder-mobile-design-ds-mobile-days-sheet-BsahIVaB.mjs.map +0 -1
- package/fesm2022/propbinder-mobile-design-ds-mobile-duration-sheet-Dun-o-dR.mjs.map +0 -1
- package/fesm2022/propbinder-mobile-design-ds-mobile-time-sheet-B9Ny4wM7.mjs.map +0 -1
package/styles/ionic.css
CHANGED
|
@@ -1,989 +1,989 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ionic-Specific Styles
|
|
3
|
-
*
|
|
4
|
-
* This file contains all styles that are specific to Ionic Framework and mobile app usage.
|
|
5
|
-
* These styles are not needed for web-only or design system library builds.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/* Core CSS required for Ionic components to work properly */
|
|
9
|
-
@import "@ionic/angular/css/core.css";
|
|
10
|
-
@import "@ionic/angular/css/structure.css";
|
|
11
|
-
@import "@ionic/angular/css/typography.css";
|
|
12
|
-
|
|
13
|
-
/* Swiper styles for image carousels */
|
|
14
|
-
@import 'swiper/css';
|
|
15
|
-
@import 'swiper/css/navigation';
|
|
16
|
-
@import 'swiper/css/pagination';
|
|
17
|
-
|
|
18
|
-
/* Mobile common styles */
|
|
19
|
-
/* Note: mobile-common.css is included in @propbinder/design-system/styles/globals.css */
|
|
20
|
-
|
|
21
|
-
/* Italic font faces (upright weights are declared in @propbinder/design-system/styles/globals.css) */
|
|
22
|
-
@font-face {
|
|
23
|
-
font-family: "Brockmann";
|
|
24
|
-
src: url("/Brockmann/brockmann-regularitalic-webfont.woff2") format("woff2");
|
|
25
|
-
font-weight: 400;
|
|
26
|
-
font-style: italic;
|
|
27
|
-
font-display: swap;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
@font-face {
|
|
31
|
-
font-family: "Brockmann";
|
|
32
|
-
src: url("/Brockmann/brockmann-mediumitalic-webfont.woff2") format("woff2");
|
|
33
|
-
font-weight: 500;
|
|
34
|
-
font-style: italic;
|
|
35
|
-
font-display: swap;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
@font-face {
|
|
39
|
-
font-family: "Brockmann";
|
|
40
|
-
src: url("/Brockmann/brockmann-semibolditalic-webfont.woff2") format("woff2");
|
|
41
|
-
font-weight: 600;
|
|
42
|
-
font-style: italic;
|
|
43
|
-
font-display: swap;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/* Ionic-Specific CSS Variables */
|
|
47
|
-
:root {
|
|
48
|
-
/* Mobile-specific brand color for headers/backgrounds */
|
|
49
|
-
--color-brand-secondary: #221a4c;
|
|
50
|
-
|
|
51
|
-
/* Ionic Keyboard Appearance - force light theme */
|
|
52
|
-
--ion-keyboard-color: #ffffff;
|
|
53
|
-
--ion-keyboard-background: #ffffff;
|
|
54
|
-
--ion-keyboard-text-color: #000000;
|
|
55
|
-
color-scheme: light;
|
|
56
|
-
|
|
57
|
-
/* Layout - Mobile Navigation */
|
|
58
|
-
--mobile-tab-bar-height: 64px;
|
|
59
|
-
--mobile-content-spacing: 20px;
|
|
60
|
-
|
|
61
|
-
/* Tab Bar Height - Used for FAB positioning and other layout calculations */
|
|
62
|
-
/* This is set by ds-mobile-tab-bar and consumed by ds-mobile-fab */
|
|
63
|
-
--ds-tab-bar-height: 64px;
|
|
64
|
-
|
|
65
|
-
/* ============================================
|
|
66
|
-
SPRING ANIMATIONS
|
|
67
|
-
Physics-based easing curves with natural bounce
|
|
68
|
-
============================================ */
|
|
69
|
-
|
|
70
|
-
/* Spring Curves */
|
|
71
|
-
--spring-curve-bouncy: linear(0, 0.0209, 0.0772, 0.1598, 0.2602, 0.3709, 0.4854, 0.5984, 0.7056, 0.8038, 0.8908, 0.9653, 1.0267, 1.075, 1.111, 1.1354, 1.1497, 1.1551, 1.1532, 1.1455, 1.1335, 1.1184, 1.1014, 1.0837, 1.066, 1.0492, 1.0336, 1.0197, 1.0077, 0.9977, 0.9898, 0.9838, 0.9796, 0.9771, 0.976, 0.9761, 0.9771, 0.9788, 0.9811, 0.9837, 0.9864, 0.9892, 0.9918, 0.9943, 0.9965, 0.9984, 1.0001, 1.0013, 1.0023, 1.003, 1.0035, 1);
|
|
72
|
-
|
|
73
|
-
--spring-curve-gentle: linear(0, 0.2, 0.5, 0.8, 1.02, 1.01, 1);
|
|
74
|
-
|
|
75
|
-
--spring-curve-snappy: linear(0, 0.3, 0.7, 1.05, 0.98, 1);
|
|
76
|
-
|
|
77
|
-
--spring-curve-smooth: linear(0, 0.215, 0.61, 0.855, 1);
|
|
78
|
-
|
|
79
|
-
/* Spring Durations */
|
|
80
|
-
--spring-duration-fast: 400ms;
|
|
81
|
-
--spring-duration-medium: 700ms;
|
|
82
|
-
--spring-duration-slow: 1000ms;
|
|
83
|
-
|
|
84
|
-
/* Spring Presets (curve + duration) */
|
|
85
|
-
--spring-bouncy: var(--spring-duration-medium) var(--spring-curve-bouncy);
|
|
86
|
-
--spring-gentle: var(--spring-duration-fast) var(--spring-curve-gentle);
|
|
87
|
-
--spring-snappy: var(--spring-duration-fast) var(--spring-curve-snappy);
|
|
88
|
-
--spring-smooth: var(--spring-duration-medium) var(--spring-curve-smooth);
|
|
89
|
-
|
|
90
|
-
/* ============================================
|
|
91
|
-
SAFE AREA / STATUS BAR CONFIGURATION
|
|
92
|
-
|
|
93
|
-
Controls how sheets and modals position relative to the status bar.
|
|
94
|
-
|
|
95
|
-
IMPORTANT: This works with StatusBar.setOverlaysWebView() in app.ts
|
|
96
|
-
- With overlay: false (default) - system handles status bar, use safe-area-inset-top
|
|
97
|
-
- With overlay: true - WebView extends under status bar
|
|
98
|
-
|
|
99
|
-
To change behavior, update BOTH:
|
|
100
|
-
1. app.ts: StatusBar.setOverlaysWebView({ overlay: true/false })
|
|
101
|
-
2. These CSS variables below
|
|
102
|
-
============================================ */
|
|
103
|
-
|
|
104
|
-
/* Normalized safe-area variables consumed by app layout styles.
|
|
105
|
-
Defaults to env() and can be overridden at runtime from SafeArea plugin. */
|
|
106
|
-
--app-safe-top: env(safe-area-inset-top, 0px);
|
|
107
|
-
--app-safe-right: env(safe-area-inset-right, 0px);
|
|
108
|
-
--app-safe-bottom: env(safe-area-inset-bottom, 0px);
|
|
109
|
-
--app-safe-left: env(safe-area-inset-left, 0px);
|
|
110
|
-
|
|
111
|
-
/* Sheet/modal top offset - positions content below status bar area */
|
|
112
|
-
/* Uses max() to ensure at least 32px offset even on devices without notch */
|
|
113
|
-
/* +12px adds breathing room below the status bar */
|
|
114
|
-
--app-sheet-top-offset: calc(max(32px, var(--app-safe-top, 32px)) + 12px);
|
|
115
|
-
|
|
116
|
-
/* Header top offset - used to fine-tune header position on iOS with overlay: true */
|
|
117
|
-
/* On web (no safe area), this should be 0px. On iOS, it compensates for status bar overlap */
|
|
118
|
-
--app-header-top-offset: max(0px, calc(var(--app-safe-top, 0px) - 16px));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/* Android-only top safe-area tuning:
|
|
122
|
-
add 8px extra breathing room above header/sheet content. */
|
|
123
|
-
.plt-android {
|
|
124
|
-
--app-sheet-top-offset: calc(max(32px, var(--app-safe-top, 32px)) + 20px);
|
|
125
|
-
--app-header-top-offset: max(0px, calc(var(--app-safe-top, 0px) - 8px));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/* Global Styles for Mobile App */
|
|
129
|
-
html {
|
|
130
|
-
/* Safe Area Variables - set here according to Ionic documentation */
|
|
131
|
-
--ion-safe-area-top: env(safe-area-inset-top);
|
|
132
|
-
--ion-safe-area-bottom: env(safe-area-inset-bottom);
|
|
133
|
-
--ion-safe-area-left: env(safe-area-inset-left);
|
|
134
|
-
--ion-safe-area-right: env(safe-area-inset-right);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
html,
|
|
138
|
-
body {
|
|
139
|
-
/* Background color must match theme-color for iOS PWA status bar */
|
|
140
|
-
background-color: var(--color-header-surface);
|
|
141
|
-
color: var(--text-color-default-primary);
|
|
142
|
-
font-family: 'Brockmann', system-ui, -apple-system, sans-serif;
|
|
143
|
-
|
|
144
|
-
/* Ensure full height for iOS PWA */
|
|
145
|
-
height: 100%;
|
|
146
|
-
width: 100%;
|
|
147
|
-
margin: 0;
|
|
148
|
-
padding: 0;
|
|
149
|
-
|
|
150
|
-
/* Don't add padding to html/body - let Ionic components handle safe areas */
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/* iOS-specific: Purple background for overshoot areas to match dark header */
|
|
154
|
-
.plt-ios html,
|
|
155
|
-
.plt-ios body {
|
|
156
|
-
background-color: var(--color-header-surface) !important;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.plt-ios ion-app {
|
|
160
|
-
background-color: var(--color-header-surface) !important;
|
|
161
|
-
height: 100dvh;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
ion-app {
|
|
165
|
-
height: 100dvh;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/* When modal opens, ensure purple background is maintained */
|
|
169
|
-
body.backdrop-no-scroll {
|
|
170
|
-
background-color: var(--color-header-surface) !important;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/* Ionic Component Styles - Map to Design System */
|
|
174
|
-
/* These styles apply Ionic's CSS variables to match our design system */
|
|
175
|
-
|
|
176
|
-
:root {
|
|
177
|
-
/* Override Ionic's primary color to use our brand color */
|
|
178
|
-
/* Note: RGB values and hover/active states are set dynamically by WhitelabelService */
|
|
179
|
-
--ion-color-primary: var(--color-accent);
|
|
180
|
-
--ion-color-primary-contrast: var(--color-on-accent);
|
|
181
|
-
--ion-color-primary-shade: var(--color-accent-hover);
|
|
182
|
-
--ion-color-primary-tint: var(--color-accent);
|
|
183
|
-
|
|
184
|
-
/* Ionic component defaults */
|
|
185
|
-
--ion-background-color: var(--color-header-surface);
|
|
186
|
-
--ion-text-color: var(--text-color-default-primary);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
ion-header {
|
|
190
|
-
box-shadow: none;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
ion-toolbar {
|
|
194
|
-
--background: transparent;
|
|
195
|
-
--border-width: 0;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
ion-content {
|
|
199
|
-
--background: var(--color-background-neutral-primary);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/* iOS: Set base ion-content background to brand secondary for pages */
|
|
203
|
-
.plt-ios ion-content {
|
|
204
|
-
background: var(--color-header-surface) !important;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/* iOS: Also target any inner background parts */
|
|
208
|
-
.plt-ios ion-content::part(background) {
|
|
209
|
-
background: var(--color-header-surface) !important;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/* iOS: Target the inner container if it exists */
|
|
213
|
-
.plt-ios ion-content .inner-scroll {
|
|
214
|
-
background: var(--color-header-surface) !important;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/* Default: Block browser overscroll on all platforms */
|
|
218
|
-
ion-content::part(scroll) {
|
|
219
|
-
overscroll-behavior-y: none;
|
|
220
|
-
-webkit-overflow-scrolling: touch;
|
|
221
|
-
/* Hide scrollbar while maintaining scroll functionality */
|
|
222
|
-
scrollbar-width: none;
|
|
223
|
-
/* Firefox */
|
|
224
|
-
-ms-overflow-style: none;
|
|
225
|
-
/* IE/Edge */
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/* Hide scrollbar for WebKit browsers (Chrome, Safari, iOS) */
|
|
229
|
-
ion-content::part(scroll)::-webkit-scrollbar {
|
|
230
|
-
display: none;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
.ion-page {
|
|
234
|
-
overscroll-behavior-y: none;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/* iOS-specific: Enable native scroll overshoot/bounce effect */
|
|
238
|
-
.plt-ios ion-content::part(scroll) {
|
|
239
|
-
overscroll-behavior-y: auto;
|
|
240
|
-
/* iOS: Set scroll container background to match brand secondary */
|
|
241
|
-
/* This prevents white background from showing when header fades on scroll */
|
|
242
|
-
background: var(--color-header-surface) !important;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
.plt-ios .ion-page {
|
|
246
|
-
overscroll-behavior-y: auto;
|
|
247
|
-
/* Set page background to brand secondary to match header on iOS */
|
|
248
|
-
background: var(--color-header-surface) !important;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/* iOS-specific: Override ion-content background for pages (not modals) */
|
|
252
|
-
.plt-ios .ion-page > ion-content {
|
|
253
|
-
--background: var(--color-header-surface);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/* iOS: Ensure modal content stays with neutral background */
|
|
257
|
-
.plt-ios ion-modal ion-content::part(scroll) {
|
|
258
|
-
background: var(--color-background-neutral-primary) !important;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
.plt-ios ion-modal ion-content {
|
|
262
|
-
background: var(--color-background-neutral-primary) !important;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
.plt-ios ion-modal ion-content::part(background) {
|
|
266
|
-
background: var(--color-background-neutral-primary) !important;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
.plt-ios ion-modal ion-content .inner-scroll {
|
|
270
|
-
background: var(--color-background-neutral-primary) !important;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/* Exception: Lightbox modals should have black background */
|
|
274
|
-
.plt-ios ion-modal .lightbox-content.pdf-viewer,
|
|
275
|
-
.plt-ios ion-modal .lightbox-content.pdf-viewer::part(scroll),
|
|
276
|
-
.plt-ios ion-modal .lightbox-content.pdf-viewer::part(background),
|
|
277
|
-
.plt-ios ion-modal .lightbox-content.pdf-viewer .inner-scroll {
|
|
278
|
-
background: #000000 !important;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/* Also ensure lightbox is black on all platforms */
|
|
282
|
-
ion-modal .lightbox-content.pdf-viewer {
|
|
283
|
-
--background: #000000 !important;
|
|
284
|
-
background: #000000 !important;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
ion-modal .lightbox-content.pdf-viewer::part(scroll),
|
|
288
|
-
ion-modal .lightbox-content.pdf-viewer::part(background),
|
|
289
|
-
ion-modal .lightbox-content.pdf-viewer .inner-scroll {
|
|
290
|
-
background: #000000 !important;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
ion-footer {
|
|
294
|
-
box-shadow: none;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
ion-router-outlet {
|
|
298
|
-
--background: var(--color-header-surface);
|
|
299
|
-
background: var(--color-header-surface);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
ion-refresher {
|
|
303
|
-
--background: transparent;
|
|
304
|
-
--color: white;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
ion-refresher-content {
|
|
308
|
-
--color: white;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/* Force spinner color to brand */
|
|
312
|
-
ion-spinner {
|
|
313
|
-
--color: var(--color-accent) !important;
|
|
314
|
-
color: var(--color-accent) !important;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
ion-modal:not(.ds-bottom-sheet) {
|
|
318
|
-
--background: var(--color-background-neutral-primary);
|
|
319
|
-
--border-radius: 16px;
|
|
320
|
-
--box-shadow: none;
|
|
321
|
-
/* Prevent modal from resizing when keyboard appears */
|
|
322
|
-
height: 100% !important;
|
|
323
|
-
max-height: 100vh !important;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/* Prevent modal container from resizing when keyboard appears */
|
|
327
|
-
ion-modal:not(.ds-bottom-sheet)::part(content) {
|
|
328
|
-
position: fixed !important;
|
|
329
|
-
height: 100% !important;
|
|
330
|
-
max-height: 100vh !important;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/* Prevent ion-content inside modals from resizing */
|
|
334
|
-
ion-modal ion-content {
|
|
335
|
-
--keyboard-offset: 0px !important;
|
|
336
|
-
height: 100% !important;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/* Keep neutral cursor over dismissable backdrop areas */
|
|
340
|
-
ion-modal::part(backdrop) {
|
|
341
|
-
cursor: default !important;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
ion-action-sheet {
|
|
345
|
-
--background: var(--color-background-neutral-primary);
|
|
346
|
-
--color: var(--text-color-default-primary);
|
|
347
|
-
--button-background: var(--color-background-neutral-secondary);
|
|
348
|
-
--button-background-hover: var(--color-background-neutral-tertiary);
|
|
349
|
-
--button-background-selected: var(--color-accent);
|
|
350
|
-
--button-color: var(--text-color-default-primary);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
ion-action-sheet::part(group) {
|
|
354
|
-
border-radius: 16px 16px 0 0;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
ion-toast {
|
|
358
|
-
--background: var(--color-background-neutral-strong);
|
|
359
|
-
--color: var(--text-color-default-primary-inverse);
|
|
360
|
-
--border-radius: 8px;
|
|
361
|
-
--box-shadow: var(--box-shadow-lg);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/* ============================================
|
|
365
|
-
BOTTOM SHEET MODAL STYLES
|
|
366
|
-
Based on Ionic 6 bottom sheet presentation
|
|
367
|
-
============================================ */
|
|
368
|
-
|
|
369
|
-
/* Base bottom sheet styling */
|
|
370
|
-
.ds-bottom-sheet {
|
|
371
|
-
--border-radius: 16px;
|
|
372
|
-
--box-shadow: none;
|
|
373
|
-
--backdrop-opacity: 0.4;
|
|
374
|
-
transition: --backdrop-opacity 0.3s ease;
|
|
375
|
-
/* Modal at top:0 so backdrop covers full screen including status bar */
|
|
376
|
-
top: 0;
|
|
377
|
-
height: 100%;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/* Backdrop styling */
|
|
381
|
-
.ds-bottom-sheet::part(backdrop) {
|
|
382
|
-
background: rgba(0, 0, 0, var(--backdrop-opacity, 0.6));
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/* Handle customization */
|
|
386
|
-
.ds-bottom-sheet::part(handle) {
|
|
387
|
-
background: var(--border-color-default);
|
|
388
|
-
width: 36px;
|
|
389
|
-
height: 4px;
|
|
390
|
-
border-radius: 2px;
|
|
391
|
-
margin-top: 4px;
|
|
392
|
-
margin-bottom: 8px;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/* Content area styling */
|
|
396
|
-
.ds-bottom-sheet::part(content) {
|
|
397
|
-
/* Offset content below status bar (backdrop still covers full screen) */
|
|
398
|
-
margin-top: var(--app-sheet-top-offset);
|
|
399
|
-
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
|
400
|
-
background: var(--color-background-neutral-primary, #ffffff);
|
|
401
|
-
box-shadow: var(--box-shadow);
|
|
402
|
-
position: absolute !important;
|
|
403
|
-
top: 0 !important;
|
|
404
|
-
bottom: 0 !important;
|
|
405
|
-
height: 100% !important;
|
|
406
|
-
max-height: 100% !important;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/* Remove border radius when fully expanded */
|
|
410
|
-
@media (min-height: 768px) {
|
|
411
|
-
.ds-bottom-sheet.modal-sheet-expanded::part(content) {
|
|
412
|
-
border-radius: 0;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
/* Facility edit: same full-height content fill as other `ds-modal-base` screens (e.g. facility detail) */
|
|
419
|
-
ion-modal.ds-modal-base.facility-edit-sheet::part(content) > * {
|
|
420
|
-
display: flex;
|
|
421
|
-
flex-direction: column;
|
|
422
|
-
height: 100%;
|
|
423
|
-
min-height: 0;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/* Ensure content scrolls properly */
|
|
427
|
-
.ds-bottom-sheet ion-content {
|
|
428
|
-
--background: transparent;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/* Better mobile experience */
|
|
432
|
-
@media (max-width: 768px) {
|
|
433
|
-
.ds-bottom-sheet::part(content) {
|
|
434
|
-
max-width: 100%;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/* Desktop centering */
|
|
439
|
-
@media (min-width: 769px) {
|
|
440
|
-
.ds-bottom-sheet::part(content) {
|
|
441
|
-
max-width: 640px;
|
|
442
|
-
margin: 0 auto;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/* ============================================
|
|
447
|
-
Dynamic Height Action Sheets
|
|
448
|
-
Make action sheets auto-size to their content
|
|
449
|
-
============================================ */
|
|
450
|
-
|
|
451
|
-
/* Auto-height modal - override base bottom sheet styles */
|
|
452
|
-
.ds-bottom-sheet.auto-height {
|
|
453
|
-
top: 0 !important;
|
|
454
|
-
height: 100% !important;
|
|
455
|
-
display: flex;
|
|
456
|
-
flex-direction: column;
|
|
457
|
-
justify-content: flex-end;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
.ds-bottom-sheet.auto-height::part(content) {
|
|
461
|
-
position: relative !important;
|
|
462
|
-
height: auto !important;
|
|
463
|
-
max-height: 80vh !important;
|
|
464
|
-
top: auto !important;
|
|
465
|
-
bottom: auto !important;
|
|
466
|
-
margin-top: auto !important;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/* Ensure action sheet component sizes naturally */
|
|
470
|
-
.ds-bottom-sheet ds-mobile-actions-bottom-sheet {
|
|
471
|
-
display: block;
|
|
472
|
-
height: auto;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/* Ensure action list scrolls if needed */
|
|
476
|
-
.ds-bottom-sheet .actions-list {
|
|
477
|
-
max-height: calc(85dvh - 80px);
|
|
478
|
-
/* Account for handle, padding, and safe area */
|
|
479
|
-
overflow-y: auto;
|
|
480
|
-
-webkit-overflow-scrolling: touch;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
/* Ensure ion-content doesn't force 100% height in sheets */
|
|
484
|
-
.ds-bottom-sheet ion-content {
|
|
485
|
-
height: auto !important;
|
|
486
|
-
--height: auto;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/* ============================================
|
|
490
|
-
Bottom Sheet Keyboard Handling
|
|
491
|
-
Uses separate --keyboard-height-sheet variable to avoid conflicts with modals.
|
|
492
|
-
The transition is always present so both keyboard-show and keyboard-hide
|
|
493
|
-
animate smoothly instead of snapping.
|
|
494
|
-
============================================ */
|
|
495
|
-
|
|
496
|
-
.ds-bottom-sheet.auto-height {
|
|
497
|
-
transition: transform 0.28s cubic-bezier(0.2, 0.8, 0.4, 1);
|
|
498
|
-
transform: translateY(calc(-1 * var(--keyboard-height-sheet, 0px)));
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
/* ============================================
|
|
502
|
-
Mobile Modal Styles (Generic Modal Service)
|
|
503
|
-
============================================ */
|
|
504
|
-
|
|
505
|
-
/**
|
|
506
|
-
* Mobile Modal Styles
|
|
507
|
-
*
|
|
508
|
-
* Global styles for modals opened via DsMobileModalService.
|
|
509
|
-
* These styles are applied to all modals and handle different presentation styles.
|
|
510
|
-
*/
|
|
511
|
-
|
|
512
|
-
/* Base modal styles */
|
|
513
|
-
.ds-mobile-modal {
|
|
514
|
-
--background: var(--color-background-neutral-primary, #ffffff);
|
|
515
|
-
--border-radius: 16px;
|
|
516
|
-
--box-shadow: none;
|
|
517
|
-
--max-width: 640px;
|
|
518
|
-
--width: 100%;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
.ds-mobile-modal::part(content) {
|
|
522
|
-
max-width: 640px;
|
|
523
|
-
margin: 0 auto;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
/* Fullscreen presentation */
|
|
527
|
-
.ds-modal-fullscreen {
|
|
528
|
-
--width: 100%;
|
|
529
|
-
--height: 100%;
|
|
530
|
-
--border-radius: 0;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
.ds-modal-fullscreen::part(content) {
|
|
534
|
-
border-radius: 0;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/* Card presentation (default iOS modal style) */
|
|
538
|
-
.ds-modal-card {
|
|
539
|
-
--width: 100%;
|
|
540
|
-
--max-width: 640px;
|
|
541
|
-
--height: auto;
|
|
542
|
-
--max-height: 90%;
|
|
543
|
-
--border-radius: 16px 16px 0 0;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
.ds-modal-card::part(content) {
|
|
547
|
-
border-radius: 16px 16px 0 0;
|
|
548
|
-
box-shadow: var(--box-shadow);
|
|
549
|
-
max-width: 640px;
|
|
550
|
-
margin: 0 auto;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/* Sheet presentation (bottom sheet with breakpoints) */
|
|
554
|
-
.ds-modal-sheet {
|
|
555
|
-
--width: 100%;
|
|
556
|
-
--max-width: 640px;
|
|
557
|
-
--height: auto;
|
|
558
|
-
--border-radius: 16px 16px 0 0;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
.ds-modal-sheet::part(content) {
|
|
562
|
-
border-radius: 16px 16px 0 0;
|
|
563
|
-
box-shadow: var(--box-shadow);
|
|
564
|
-
max-width: 640px;
|
|
565
|
-
margin: 0 auto;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
.ds-modal-sheet::part(handle) {
|
|
569
|
-
background: var(--border-color-default);
|
|
570
|
-
width: 36px;
|
|
571
|
-
height: 4px;
|
|
572
|
-
border-radius: 2px;
|
|
573
|
-
margin: 8px auto 0;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
/* Backdrop styles */
|
|
577
|
-
.ds-mobile-modal::part(backdrop) {
|
|
578
|
-
background: rgba(0, 0, 0, 0.4);
|
|
579
|
-
backdrop-filter: blur(4px);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
/* Animations */
|
|
583
|
-
.ds-mobile-modal.modal-card-enter-active,
|
|
584
|
-
.ds-mobile-modal.modal-sheet-enter-active {
|
|
585
|
-
animation: slideUpModal 0.5s ease-out;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
.ds-mobile-modal.modal-card-leave-active,
|
|
589
|
-
.ds-mobile-modal.modal-sheet-leave-active {
|
|
590
|
-
animation: slideDownModal 0.8s ease-in;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
.ds-mobile-modal.modal-fullscreen-enter-active {
|
|
594
|
-
animation: fadeInModal 0.4s ease-out;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
.ds-mobile-modal.modal-fullscreen-leave-active {
|
|
598
|
-
animation: fadeOutModal 0.4s ease-in;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
@keyframes slideUpModal {
|
|
602
|
-
from {
|
|
603
|
-
transform: translateY(100%);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
to {
|
|
607
|
-
transform: translateY(0);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
@keyframes slideDownModal {
|
|
612
|
-
from {
|
|
613
|
-
transform: translateY(0);
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
to {
|
|
617
|
-
transform: translateY(100%);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
@keyframes fadeInModal {
|
|
622
|
-
from {
|
|
623
|
-
opacity: 0;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
to {
|
|
627
|
-
opacity: 1;
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
@keyframes fadeOutModal {
|
|
632
|
-
from {
|
|
633
|
-
opacity: 1;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
to {
|
|
637
|
-
opacity: 0;
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
/* Safe area handling for notched devices */
|
|
642
|
-
@supports (padding: env(safe-area-inset-top)) {
|
|
643
|
-
.ds-modal-fullscreen::part(content) {
|
|
644
|
-
padding-top: env(safe-area-inset-top);
|
|
645
|
-
padding-bottom: env(safe-area-inset-bottom);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
/* Dark mode support removed - modals should not change based on system dark mode */
|
|
650
|
-
|
|
651
|
-
/* Accessibility: Reduced motion */
|
|
652
|
-
@media (prefers-reduced-motion: reduce) {
|
|
653
|
-
|
|
654
|
-
.ds-mobile-modal.modal-card-enter-active,
|
|
655
|
-
.ds-mobile-modal.modal-card-leave-active,
|
|
656
|
-
.ds-mobile-modal.modal-sheet-enter-active,
|
|
657
|
-
.ds-mobile-modal.modal-sheet-leave-active,
|
|
658
|
-
.ds-mobile-modal.modal-fullscreen-enter-active,
|
|
659
|
-
.ds-mobile-modal.modal-fullscreen-leave-active {
|
|
660
|
-
animation: none;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
/* ============================================
|
|
665
|
-
ION-TAB-BUTTON CUSTOMIZATION
|
|
666
|
-
============================================ */
|
|
667
|
-
|
|
668
|
-
/* Set gap for button-inner to control spacing between icon and label */
|
|
669
|
-
ion-tab-button::part(native) {
|
|
670
|
-
gap: 2px;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
/* ============================================
|
|
674
|
-
Base Modal Styles (Shared across all modals)
|
|
675
|
-
============================================ */
|
|
676
|
-
|
|
677
|
-
ion-modal.ds-modal-base {
|
|
678
|
-
--background: var(--color-background-neutral-primary, #ffffff);
|
|
679
|
-
--width: 100%;
|
|
680
|
-
--max-width: 640px;
|
|
681
|
-
--height: 100dvh;
|
|
682
|
-
/* Full viewport height - content top offset creates gap */
|
|
683
|
-
--border-radius: 16px 16px 0 0;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
ion-modal.ds-modal-base::part(content) {
|
|
687
|
-
border-radius: 16px 16px 0 0;
|
|
688
|
-
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
|
|
689
|
-
background: var(--color-background-neutral-primary, #ffffff);
|
|
690
|
-
max-width: 640px;
|
|
691
|
-
margin: 0 auto;
|
|
692
|
-
/* Use top positioning instead of margin-top to work with fixed positioning */
|
|
693
|
-
top: var(--app-sheet-top-offset) !important;
|
|
694
|
-
height: calc(100% - var(--app-sheet-top-offset)) !important;
|
|
695
|
-
max-height: calc(100dvh - var(--app-sheet-top-offset)) !important;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
/* Auto-height support for base modals */
|
|
699
|
-
.ds-modal-base.auto-height {
|
|
700
|
-
display: flex;
|
|
701
|
-
flex-direction: column;
|
|
702
|
-
justify-content: flex-end;
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
.ds-modal-base.auto-height::part(content) {
|
|
706
|
-
height: auto !important;
|
|
707
|
-
margin-top: auto !important;
|
|
708
|
-
position: relative !important;
|
|
709
|
-
top: auto !important;
|
|
710
|
-
max-height: calc(100dvh - var(--app-sheet-top-offset, 24px)) !important;
|
|
711
|
-
bottom: 0 !important;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
.ds-modal-base.auto-height ion-content {
|
|
715
|
-
height: auto !important;
|
|
716
|
-
--height: auto;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
.ds-modal-base::part(backdrop) {
|
|
720
|
-
background: rgba(0, 0, 0, 0.4);
|
|
721
|
-
backdrop-filter: blur(4px);
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
.ds-modal-base ion-content {
|
|
725
|
-
--background: var(--color-background-neutral-primary, #ffffff);
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
/* ============================================
|
|
729
|
-
Post Detail Modal Styles
|
|
730
|
-
============================================ */
|
|
731
|
-
|
|
732
|
-
.ds-post-detail-modal {
|
|
733
|
-
--background: var(--color-background-neutral-primary, #ffffff);
|
|
734
|
-
--width: 100%;
|
|
735
|
-
--max-width: 640px;
|
|
736
|
-
--height: 100dvh;
|
|
737
|
-
/* Full viewport height - content top offset creates gap */
|
|
738
|
-
--border-radius: 16px 16px 0 0;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
.ds-post-detail-modal::part(content) {
|
|
742
|
-
border-radius: 16px 16px 0 0;
|
|
743
|
-
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
|
|
744
|
-
background: var(--color-background-neutral-primary, #ffffff);
|
|
745
|
-
max-width: 640px;
|
|
746
|
-
margin: 0 auto;
|
|
747
|
-
/* Use top positioning instead of margin-top to work with fixed positioning */
|
|
748
|
-
top: var(--app-sheet-top-offset) !important;
|
|
749
|
-
height: calc(100% - var(--app-sheet-top-offset)) !important;
|
|
750
|
-
max-height: calc(100vh - var(--app-sheet-top-offset)) !important;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
.ds-post-detail-modal::part(backdrop) {
|
|
754
|
-
background: rgba(0, 0, 0, 0.4);
|
|
755
|
-
backdrop-filter: blur(4px);
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
.ds-post-detail-modal ion-content {
|
|
759
|
-
--background: var(--color-background-neutral-primary, #ffffff);
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
/* Control the presenting element (page behind) border radius */
|
|
763
|
-
ion-router-outlet.ion-page-hidden,
|
|
764
|
-
.ion-page.ion-page-hidden {
|
|
765
|
-
border-radius: 16px 16px 0 0 !important;
|
|
766
|
-
overflow: hidden;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
/* Override Ionic's default 10px border-radius */
|
|
770
|
-
body.backdrop-no-scroll ion-router-outlet {
|
|
771
|
-
border-radius: 16px 16px 0 0 !important;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
/* Target the router outlet when modal is active */
|
|
775
|
-
ion-router-outlet:not(.ion-page-hidden) {
|
|
776
|
-
border-radius: 16px 16px 0 0 !important;
|
|
777
|
-
transition: border-radius 0.3s ease, transform 0.3s ease;
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
/* Adjust the presenting element animation */
|
|
781
|
-
.modal-card-enter-active ion-router-outlet,
|
|
782
|
-
.modal-card-enter-active .ion-page {
|
|
783
|
-
border-radius: 16px 16px 0 0 !important;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
/* When page is scaled behind modal */
|
|
787
|
-
.ion-page-invisible {
|
|
788
|
-
border-radius: 16px 16px 0 0 !important;
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
/* Override Ionic's default presenting page styles */
|
|
792
|
-
.modal-shadow .ion-page {
|
|
793
|
-
border-radius: 16px 16px 0 0 !important;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
/* ============================================
|
|
797
|
-
More Menu Sheet
|
|
798
|
-
Mobile: sits above the bottom tab bar, clipped so
|
|
799
|
-
backdrop doesn't bleed over it.
|
|
800
|
-
Desktop: behaves like a normal bottom sheet (tab bar
|
|
801
|
-
is at the top, no special handling needed).
|
|
802
|
-
============================================ */
|
|
803
|
-
|
|
804
|
-
@media (max-width: 767px) {
|
|
805
|
-
.ds-more-menu-sheet {
|
|
806
|
-
z-index: 100 !important;
|
|
807
|
-
bottom: var(--ds-more-menu-tab-bar-offset, 64px) !important;
|
|
808
|
-
height: calc(100% - var(--ds-more-menu-tab-bar-offset, 64px)) !important;
|
|
809
|
-
overflow: hidden;
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
.ds-more-menu-sheet.auto-height {
|
|
813
|
-
padding-bottom: 0;
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
.ds-more-menu-sheet .bottom-sheet-wrapper {
|
|
818
|
-
min-height: 0 !important;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
/* Hide modal shadow when auto-height sheet is active */
|
|
822
|
-
body:has(.ds-bottom-sheet.auto-height) .modal-shadow {
|
|
823
|
-
display: none !important;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
ion-modal.show-modal ~ ion-router-outlet,
|
|
827
|
-
ion-modal.show-modal ~ * ion-router-outlet {
|
|
828
|
-
border-radius: 16px 16px 0 0 !important;
|
|
829
|
-
transform: scale(0.915) !important;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
/* Most important - override any Ionic defaults */
|
|
833
|
-
ion-router-outlet[style*="border-radius"] {
|
|
834
|
-
border-radius: 16px 16px 0 0 !important;
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
/* Target app root with modal */
|
|
838
|
-
ion-app.ion-page-hidden ion-router-outlet,
|
|
839
|
-
ion-app ion-router-outlet.ion-page-hidden {
|
|
840
|
-
border-radius: 16px 16px 0 0 !important;
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
/* Safe area handling - NOT needed here since modal positioning (--app-sheet-top-offset) handles it */
|
|
844
|
-
|
|
845
|
-
/* Dark mode support removed - modals should not change based on system dark mode */
|
|
846
|
-
|
|
847
|
-
/* ============================================
|
|
848
|
-
Chat Modal Styles
|
|
849
|
-
============================================ */
|
|
850
|
-
|
|
851
|
-
.ds-chat-modal {
|
|
852
|
-
--background: var(--color-background-neutral-primary, #ffffff);
|
|
853
|
-
--width: 100%;
|
|
854
|
-
--max-width: 640px;
|
|
855
|
-
--height: 100dvh;
|
|
856
|
-
/* Full viewport height - content top offset creates gap */
|
|
857
|
-
--border-radius: 16px 16px 0 0;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
.ds-chat-modal::part(content) {
|
|
861
|
-
border-radius: 16px 16px 0 0;
|
|
862
|
-
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
|
|
863
|
-
background: var(--color-background-neutral-primary, #ffffff);
|
|
864
|
-
max-width: 640px;
|
|
865
|
-
margin: 0 auto;
|
|
866
|
-
/* Use top positioning instead of margin-top to work with fixed positioning */
|
|
867
|
-
top: var(--app-sheet-top-offset) !important;
|
|
868
|
-
height: calc(100% - var(--app-sheet-top-offset)) !important;
|
|
869
|
-
max-height: calc(100vh - var(--app-sheet-top-offset)) !important;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
.ds-chat-modal::part(backdrop) {
|
|
873
|
-
background: rgba(0, 0, 0, 0.4);
|
|
874
|
-
backdrop-filter: blur(4px);
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
.ds-chat-modal ion-content {
|
|
878
|
-
--background: var(--color-background-neutral-primary, #ffffff);
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
/* Dark mode support removed - modals should not change based on system dark mode */
|
|
882
|
-
|
|
883
|
-
/* ============================================
|
|
884
|
-
Handbook Detail Modal Styles
|
|
885
|
-
============================================ */
|
|
886
|
-
|
|
887
|
-
.ds-handbook-detail-modal {
|
|
888
|
-
--background: var(--color-background-neutral-primary, #ffffff);
|
|
889
|
-
--width: 100%;
|
|
890
|
-
--max-width: 640px;
|
|
891
|
-
--height: 100dvh;
|
|
892
|
-
/* Use dynamic viewport height instead of 100% */
|
|
893
|
-
--border-radius: 16px 16px 0 0;
|
|
894
|
-
margin-top: var(--app-sheet-top-offset);
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
.ds-handbook-detail-modal::part(content) {
|
|
898
|
-
border-radius: 16px 16px 0 0;
|
|
899
|
-
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
|
|
900
|
-
background: var(--color-background-neutral-primary, #ffffff);
|
|
901
|
-
max-width: 640px;
|
|
902
|
-
margin: 0 auto;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
.ds-handbook-detail-modal::part(backdrop) {
|
|
906
|
-
background: rgba(0, 0, 0, 0.4);
|
|
907
|
-
backdrop-filter: blur(4px);
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
.ds-handbook-detail-modal ion-content {
|
|
911
|
-
--background: var(--color-background-neutral-primary, #ffffff);
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
/* Safe area handling - NOT needed here since modal positioning (--app-sheet-top-offset) handles it */
|
|
915
|
-
|
|
916
|
-
/* Dark mode support */
|
|
917
|
-
@media (prefers-color-scheme: dark) {
|
|
918
|
-
.ds-handbook-detail-modal {
|
|
919
|
-
--background: var(--color-background-neutral-primary-dark, #1a1a1a);
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
.ds-handbook-detail-modal::part(content) {
|
|
923
|
-
background: var(--color-background-neutral-primary-dark, #1a1a1a);
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
.ds-handbook-detail-modal::part(backdrop) {
|
|
927
|
-
background: rgba(0, 0, 0, 0.6);
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
.ds-handbook-detail-modal ion-content {
|
|
931
|
-
--background: var(--color-background-neutral-primary-dark, #1a1a1a);
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
/* ============================================
|
|
936
|
-
Whitelabel Demo Modal Styles
|
|
937
|
-
============================================ */
|
|
938
|
-
|
|
939
|
-
.ds-whitelabel-demo-modal {
|
|
940
|
-
--background: var(--color-background-neutral-primary, #ffffff);
|
|
941
|
-
--width: 100%;
|
|
942
|
-
--max-width: 640px;
|
|
943
|
-
--height: 100dvh;
|
|
944
|
-
/* Full viewport height - content top offset creates gap */
|
|
945
|
-
--border-radius: 16px 16px 0 0;
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
.ds-whitelabel-demo-modal::part(content) {
|
|
949
|
-
border-radius: 16px 16px 0 0;
|
|
950
|
-
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
|
|
951
|
-
background: var(--color-background-neutral-primary, #ffffff);
|
|
952
|
-
max-width: 640px;
|
|
953
|
-
margin: 0 auto;
|
|
954
|
-
/* Use top positioning instead of margin-top to work with fixed positioning */
|
|
955
|
-
top: var(--app-sheet-top-offset) !important;
|
|
956
|
-
height: calc(100% - var(--app-sheet-top-offset)) !important;
|
|
957
|
-
max-height: calc(100vh - var(--app-sheet-top-offset)) !important;
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
.ds-whitelabel-demo-modal::part(backdrop) {
|
|
961
|
-
background: rgba(0, 0, 0, 0.4);
|
|
962
|
-
backdrop-filter: blur(4px);
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
.ds-whitelabel-demo-modal ion-content {
|
|
966
|
-
--background: #ffffff;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
/* Dark mode support removed - modals should not change based on system dark mode */
|
|
970
|
-
|
|
971
|
-
/* ============================================
|
|
972
|
-
GLOBAL BUTTON INTERACTIVE STATES
|
|
973
|
-
Consistent button behavior across all ds-button instances
|
|
974
|
-
============================================ */
|
|
975
|
-
|
|
976
|
-
/* Base transition */
|
|
977
|
-
ds-button .btn {
|
|
978
|
-
transition: all 0.2s ease !important;
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
/* Active/pressed/click state - scale down slightly */
|
|
982
|
-
ds-button .btn:active:not(:disabled) {
|
|
983
|
-
transform: scale(0.98) !important;
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
/* FORCE hover background color directly - only for primary variant */
|
|
987
|
-
ds-button[variant="primary"]:hover .btn {
|
|
988
|
-
background-color: var(--color-accent-hover) !important;
|
|
989
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Ionic-Specific Styles
|
|
3
|
+
*
|
|
4
|
+
* This file contains all styles that are specific to Ionic Framework and mobile app usage.
|
|
5
|
+
* These styles are not needed for web-only or design system library builds.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/* Core CSS required for Ionic components to work properly */
|
|
9
|
+
@import "@ionic/angular/css/core.css";
|
|
10
|
+
@import "@ionic/angular/css/structure.css";
|
|
11
|
+
@import "@ionic/angular/css/typography.css";
|
|
12
|
+
|
|
13
|
+
/* Swiper styles for image carousels */
|
|
14
|
+
@import 'swiper/css';
|
|
15
|
+
@import 'swiper/css/navigation';
|
|
16
|
+
@import 'swiper/css/pagination';
|
|
17
|
+
|
|
18
|
+
/* Mobile common styles */
|
|
19
|
+
/* Note: mobile-common.css is included in @propbinder/design-system/styles/globals.css */
|
|
20
|
+
|
|
21
|
+
/* Italic font faces (upright weights are declared in @propbinder/design-system/styles/globals.css) */
|
|
22
|
+
@font-face {
|
|
23
|
+
font-family: "Brockmann";
|
|
24
|
+
src: url("/Brockmann/brockmann-regularitalic-webfont.woff2") format("woff2");
|
|
25
|
+
font-weight: 400;
|
|
26
|
+
font-style: italic;
|
|
27
|
+
font-display: swap;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@font-face {
|
|
31
|
+
font-family: "Brockmann";
|
|
32
|
+
src: url("/Brockmann/brockmann-mediumitalic-webfont.woff2") format("woff2");
|
|
33
|
+
font-weight: 500;
|
|
34
|
+
font-style: italic;
|
|
35
|
+
font-display: swap;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@font-face {
|
|
39
|
+
font-family: "Brockmann";
|
|
40
|
+
src: url("/Brockmann/brockmann-semibolditalic-webfont.woff2") format("woff2");
|
|
41
|
+
font-weight: 600;
|
|
42
|
+
font-style: italic;
|
|
43
|
+
font-display: swap;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Ionic-Specific CSS Variables */
|
|
47
|
+
:root {
|
|
48
|
+
/* Mobile-specific brand color for headers/backgrounds */
|
|
49
|
+
--color-brand-secondary: #221a4c;
|
|
50
|
+
|
|
51
|
+
/* Ionic Keyboard Appearance - force light theme */
|
|
52
|
+
--ion-keyboard-color: #ffffff;
|
|
53
|
+
--ion-keyboard-background: #ffffff;
|
|
54
|
+
--ion-keyboard-text-color: #000000;
|
|
55
|
+
color-scheme: light;
|
|
56
|
+
|
|
57
|
+
/* Layout - Mobile Navigation */
|
|
58
|
+
--mobile-tab-bar-height: 64px;
|
|
59
|
+
--mobile-content-spacing: 20px;
|
|
60
|
+
|
|
61
|
+
/* Tab Bar Height - Used for FAB positioning and other layout calculations */
|
|
62
|
+
/* This is set by ds-mobile-tab-bar and consumed by ds-mobile-fab */
|
|
63
|
+
--ds-tab-bar-height: 64px;
|
|
64
|
+
|
|
65
|
+
/* ============================================
|
|
66
|
+
SPRING ANIMATIONS
|
|
67
|
+
Physics-based easing curves with natural bounce
|
|
68
|
+
============================================ */
|
|
69
|
+
|
|
70
|
+
/* Spring Curves */
|
|
71
|
+
--spring-curve-bouncy: linear(0, 0.0209, 0.0772, 0.1598, 0.2602, 0.3709, 0.4854, 0.5984, 0.7056, 0.8038, 0.8908, 0.9653, 1.0267, 1.075, 1.111, 1.1354, 1.1497, 1.1551, 1.1532, 1.1455, 1.1335, 1.1184, 1.1014, 1.0837, 1.066, 1.0492, 1.0336, 1.0197, 1.0077, 0.9977, 0.9898, 0.9838, 0.9796, 0.9771, 0.976, 0.9761, 0.9771, 0.9788, 0.9811, 0.9837, 0.9864, 0.9892, 0.9918, 0.9943, 0.9965, 0.9984, 1.0001, 1.0013, 1.0023, 1.003, 1.0035, 1);
|
|
72
|
+
|
|
73
|
+
--spring-curve-gentle: linear(0, 0.2, 0.5, 0.8, 1.02, 1.01, 1);
|
|
74
|
+
|
|
75
|
+
--spring-curve-snappy: linear(0, 0.3, 0.7, 1.05, 0.98, 1);
|
|
76
|
+
|
|
77
|
+
--spring-curve-smooth: linear(0, 0.215, 0.61, 0.855, 1);
|
|
78
|
+
|
|
79
|
+
/* Spring Durations */
|
|
80
|
+
--spring-duration-fast: 400ms;
|
|
81
|
+
--spring-duration-medium: 700ms;
|
|
82
|
+
--spring-duration-slow: 1000ms;
|
|
83
|
+
|
|
84
|
+
/* Spring Presets (curve + duration) */
|
|
85
|
+
--spring-bouncy: var(--spring-duration-medium) var(--spring-curve-bouncy);
|
|
86
|
+
--spring-gentle: var(--spring-duration-fast) var(--spring-curve-gentle);
|
|
87
|
+
--spring-snappy: var(--spring-duration-fast) var(--spring-curve-snappy);
|
|
88
|
+
--spring-smooth: var(--spring-duration-medium) var(--spring-curve-smooth);
|
|
89
|
+
|
|
90
|
+
/* ============================================
|
|
91
|
+
SAFE AREA / STATUS BAR CONFIGURATION
|
|
92
|
+
|
|
93
|
+
Controls how sheets and modals position relative to the status bar.
|
|
94
|
+
|
|
95
|
+
IMPORTANT: This works with StatusBar.setOverlaysWebView() in app.ts
|
|
96
|
+
- With overlay: false (default) - system handles status bar, use safe-area-inset-top
|
|
97
|
+
- With overlay: true - WebView extends under status bar
|
|
98
|
+
|
|
99
|
+
To change behavior, update BOTH:
|
|
100
|
+
1. app.ts: StatusBar.setOverlaysWebView({ overlay: true/false })
|
|
101
|
+
2. These CSS variables below
|
|
102
|
+
============================================ */
|
|
103
|
+
|
|
104
|
+
/* Normalized safe-area variables consumed by app layout styles.
|
|
105
|
+
Defaults to env() and can be overridden at runtime from SafeArea plugin. */
|
|
106
|
+
--app-safe-top: env(safe-area-inset-top, 0px);
|
|
107
|
+
--app-safe-right: env(safe-area-inset-right, 0px);
|
|
108
|
+
--app-safe-bottom: env(safe-area-inset-bottom, 0px);
|
|
109
|
+
--app-safe-left: env(safe-area-inset-left, 0px);
|
|
110
|
+
|
|
111
|
+
/* Sheet/modal top offset - positions content below status bar area */
|
|
112
|
+
/* Uses max() to ensure at least 32px offset even on devices without notch */
|
|
113
|
+
/* +12px adds breathing room below the status bar */
|
|
114
|
+
--app-sheet-top-offset: calc(max(32px, var(--app-safe-top, 32px)) + 12px);
|
|
115
|
+
|
|
116
|
+
/* Header top offset - used to fine-tune header position on iOS with overlay: true */
|
|
117
|
+
/* On web (no safe area), this should be 0px. On iOS, it compensates for status bar overlap */
|
|
118
|
+
--app-header-top-offset: max(0px, calc(var(--app-safe-top, 0px) - 16px));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Android-only top safe-area tuning:
|
|
122
|
+
add 8px extra breathing room above header/sheet content. */
|
|
123
|
+
.plt-android {
|
|
124
|
+
--app-sheet-top-offset: calc(max(32px, var(--app-safe-top, 32px)) + 20px);
|
|
125
|
+
--app-header-top-offset: max(0px, calc(var(--app-safe-top, 0px) - 8px));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Global Styles for Mobile App */
|
|
129
|
+
html {
|
|
130
|
+
/* Safe Area Variables - set here according to Ionic documentation */
|
|
131
|
+
--ion-safe-area-top: env(safe-area-inset-top);
|
|
132
|
+
--ion-safe-area-bottom: env(safe-area-inset-bottom);
|
|
133
|
+
--ion-safe-area-left: env(safe-area-inset-left);
|
|
134
|
+
--ion-safe-area-right: env(safe-area-inset-right);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
html,
|
|
138
|
+
body {
|
|
139
|
+
/* Background color must match theme-color for iOS PWA status bar */
|
|
140
|
+
background-color: var(--color-header-surface);
|
|
141
|
+
color: var(--text-color-default-primary);
|
|
142
|
+
font-family: 'Brockmann', system-ui, -apple-system, sans-serif;
|
|
143
|
+
|
|
144
|
+
/* Ensure full height for iOS PWA */
|
|
145
|
+
height: 100%;
|
|
146
|
+
width: 100%;
|
|
147
|
+
margin: 0;
|
|
148
|
+
padding: 0;
|
|
149
|
+
|
|
150
|
+
/* Don't add padding to html/body - let Ionic components handle safe areas */
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* iOS-specific: Purple background for overshoot areas to match dark header */
|
|
154
|
+
.plt-ios html,
|
|
155
|
+
.plt-ios body {
|
|
156
|
+
background-color: var(--color-header-surface) !important;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.plt-ios ion-app {
|
|
160
|
+
background-color: var(--color-header-surface) !important;
|
|
161
|
+
height: 100dvh;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
ion-app {
|
|
165
|
+
height: 100dvh;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* When modal opens, ensure purple background is maintained */
|
|
169
|
+
body.backdrop-no-scroll {
|
|
170
|
+
background-color: var(--color-header-surface) !important;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Ionic Component Styles - Map to Design System */
|
|
174
|
+
/* These styles apply Ionic's CSS variables to match our design system */
|
|
175
|
+
|
|
176
|
+
:root {
|
|
177
|
+
/* Override Ionic's primary color to use our brand color */
|
|
178
|
+
/* Note: RGB values and hover/active states are set dynamically by WhitelabelService */
|
|
179
|
+
--ion-color-primary: var(--color-accent);
|
|
180
|
+
--ion-color-primary-contrast: var(--color-on-accent);
|
|
181
|
+
--ion-color-primary-shade: var(--color-accent-hover);
|
|
182
|
+
--ion-color-primary-tint: var(--color-accent);
|
|
183
|
+
|
|
184
|
+
/* Ionic component defaults */
|
|
185
|
+
--ion-background-color: var(--color-header-surface);
|
|
186
|
+
--ion-text-color: var(--text-color-default-primary);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
ion-header {
|
|
190
|
+
box-shadow: none;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
ion-toolbar {
|
|
194
|
+
--background: transparent;
|
|
195
|
+
--border-width: 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
ion-content {
|
|
199
|
+
--background: var(--color-background-neutral-primary);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* iOS: Set base ion-content background to brand secondary for pages */
|
|
203
|
+
.plt-ios ion-content {
|
|
204
|
+
background: var(--color-header-surface) !important;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* iOS: Also target any inner background parts */
|
|
208
|
+
.plt-ios ion-content::part(background) {
|
|
209
|
+
background: var(--color-header-surface) !important;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* iOS: Target the inner container if it exists */
|
|
213
|
+
.plt-ios ion-content .inner-scroll {
|
|
214
|
+
background: var(--color-header-surface) !important;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* Default: Block browser overscroll on all platforms */
|
|
218
|
+
ion-content::part(scroll) {
|
|
219
|
+
overscroll-behavior-y: none;
|
|
220
|
+
-webkit-overflow-scrolling: touch;
|
|
221
|
+
/* Hide scrollbar while maintaining scroll functionality */
|
|
222
|
+
scrollbar-width: none;
|
|
223
|
+
/* Firefox */
|
|
224
|
+
-ms-overflow-style: none;
|
|
225
|
+
/* IE/Edge */
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* Hide scrollbar for WebKit browsers (Chrome, Safari, iOS) */
|
|
229
|
+
ion-content::part(scroll)::-webkit-scrollbar {
|
|
230
|
+
display: none;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.ion-page {
|
|
234
|
+
overscroll-behavior-y: none;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* iOS-specific: Enable native scroll overshoot/bounce effect */
|
|
238
|
+
.plt-ios ion-content::part(scroll) {
|
|
239
|
+
overscroll-behavior-y: auto;
|
|
240
|
+
/* iOS: Set scroll container background to match brand secondary */
|
|
241
|
+
/* This prevents white background from showing when header fades on scroll */
|
|
242
|
+
background: var(--color-header-surface) !important;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.plt-ios .ion-page {
|
|
246
|
+
overscroll-behavior-y: auto;
|
|
247
|
+
/* Set page background to brand secondary to match header on iOS */
|
|
248
|
+
background: var(--color-header-surface) !important;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* iOS-specific: Override ion-content background for pages (not modals) */
|
|
252
|
+
.plt-ios .ion-page > ion-content {
|
|
253
|
+
--background: var(--color-header-surface);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* iOS: Ensure modal content stays with neutral background */
|
|
257
|
+
.plt-ios ion-modal ion-content::part(scroll) {
|
|
258
|
+
background: var(--color-background-neutral-primary) !important;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.plt-ios ion-modal ion-content {
|
|
262
|
+
background: var(--color-background-neutral-primary) !important;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.plt-ios ion-modal ion-content::part(background) {
|
|
266
|
+
background: var(--color-background-neutral-primary) !important;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.plt-ios ion-modal ion-content .inner-scroll {
|
|
270
|
+
background: var(--color-background-neutral-primary) !important;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* Exception: Lightbox modals should have black background */
|
|
274
|
+
.plt-ios ion-modal .lightbox-content.pdf-viewer,
|
|
275
|
+
.plt-ios ion-modal .lightbox-content.pdf-viewer::part(scroll),
|
|
276
|
+
.plt-ios ion-modal .lightbox-content.pdf-viewer::part(background),
|
|
277
|
+
.plt-ios ion-modal .lightbox-content.pdf-viewer .inner-scroll {
|
|
278
|
+
background: #000000 !important;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* Also ensure lightbox is black on all platforms */
|
|
282
|
+
ion-modal .lightbox-content.pdf-viewer {
|
|
283
|
+
--background: #000000 !important;
|
|
284
|
+
background: #000000 !important;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
ion-modal .lightbox-content.pdf-viewer::part(scroll),
|
|
288
|
+
ion-modal .lightbox-content.pdf-viewer::part(background),
|
|
289
|
+
ion-modal .lightbox-content.pdf-viewer .inner-scroll {
|
|
290
|
+
background: #000000 !important;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
ion-footer {
|
|
294
|
+
box-shadow: none;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
ion-router-outlet {
|
|
298
|
+
--background: var(--color-header-surface);
|
|
299
|
+
background: var(--color-header-surface);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
ion-refresher {
|
|
303
|
+
--background: transparent;
|
|
304
|
+
--color: white;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
ion-refresher-content {
|
|
308
|
+
--color: white;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* Force spinner color to brand */
|
|
312
|
+
ion-spinner {
|
|
313
|
+
--color: var(--color-accent) !important;
|
|
314
|
+
color: var(--color-accent) !important;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
ion-modal:not(.ds-bottom-sheet) {
|
|
318
|
+
--background: var(--color-background-neutral-primary);
|
|
319
|
+
--border-radius: 16px;
|
|
320
|
+
--box-shadow: none;
|
|
321
|
+
/* Prevent modal from resizing when keyboard appears */
|
|
322
|
+
height: 100% !important;
|
|
323
|
+
max-height: 100vh !important;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/* Prevent modal container from resizing when keyboard appears */
|
|
327
|
+
ion-modal:not(.ds-bottom-sheet)::part(content) {
|
|
328
|
+
position: fixed !important;
|
|
329
|
+
height: 100% !important;
|
|
330
|
+
max-height: 100vh !important;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* Prevent ion-content inside modals from resizing */
|
|
334
|
+
ion-modal ion-content {
|
|
335
|
+
--keyboard-offset: 0px !important;
|
|
336
|
+
height: 100% !important;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* Keep neutral cursor over dismissable backdrop areas */
|
|
340
|
+
ion-modal::part(backdrop) {
|
|
341
|
+
cursor: default !important;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
ion-action-sheet {
|
|
345
|
+
--background: var(--color-background-neutral-primary);
|
|
346
|
+
--color: var(--text-color-default-primary);
|
|
347
|
+
--button-background: var(--color-background-neutral-secondary);
|
|
348
|
+
--button-background-hover: var(--color-background-neutral-tertiary);
|
|
349
|
+
--button-background-selected: var(--color-accent);
|
|
350
|
+
--button-color: var(--text-color-default-primary);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
ion-action-sheet::part(group) {
|
|
354
|
+
border-radius: 16px 16px 0 0;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
ion-toast {
|
|
358
|
+
--background: var(--color-background-neutral-strong);
|
|
359
|
+
--color: var(--text-color-default-primary-inverse);
|
|
360
|
+
--border-radius: 8px;
|
|
361
|
+
--box-shadow: var(--box-shadow-lg);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* ============================================
|
|
365
|
+
BOTTOM SHEET MODAL STYLES
|
|
366
|
+
Based on Ionic 6 bottom sheet presentation
|
|
367
|
+
============================================ */
|
|
368
|
+
|
|
369
|
+
/* Base bottom sheet styling */
|
|
370
|
+
.ds-bottom-sheet {
|
|
371
|
+
--border-radius: 16px;
|
|
372
|
+
--box-shadow: none;
|
|
373
|
+
--backdrop-opacity: 0.4;
|
|
374
|
+
transition: --backdrop-opacity 0.3s ease;
|
|
375
|
+
/* Modal at top:0 so backdrop covers full screen including status bar */
|
|
376
|
+
top: 0;
|
|
377
|
+
height: 100%;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* Backdrop styling */
|
|
381
|
+
.ds-bottom-sheet::part(backdrop) {
|
|
382
|
+
background: rgba(0, 0, 0, var(--backdrop-opacity, 0.6));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/* Handle customization */
|
|
386
|
+
.ds-bottom-sheet::part(handle) {
|
|
387
|
+
background: var(--border-color-default);
|
|
388
|
+
width: 36px;
|
|
389
|
+
height: 4px;
|
|
390
|
+
border-radius: 2px;
|
|
391
|
+
margin-top: 4px;
|
|
392
|
+
margin-bottom: 8px;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Content area styling */
|
|
396
|
+
.ds-bottom-sheet::part(content) {
|
|
397
|
+
/* Offset content below status bar (backdrop still covers full screen) */
|
|
398
|
+
margin-top: var(--app-sheet-top-offset);
|
|
399
|
+
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
|
400
|
+
background: var(--color-background-neutral-primary, #ffffff);
|
|
401
|
+
box-shadow: var(--box-shadow);
|
|
402
|
+
position: absolute !important;
|
|
403
|
+
top: 0 !important;
|
|
404
|
+
bottom: 0 !important;
|
|
405
|
+
height: 100% !important;
|
|
406
|
+
max-height: 100% !important;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* Remove border radius when fully expanded */
|
|
410
|
+
@media (min-height: 768px) {
|
|
411
|
+
.ds-bottom-sheet.modal-sheet-expanded::part(content) {
|
|
412
|
+
border-radius: 0;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
/* Facility edit: same full-height content fill as other `ds-modal-base` screens (e.g. facility detail) */
|
|
419
|
+
ion-modal.ds-modal-base.facility-edit-sheet::part(content) > * {
|
|
420
|
+
display: flex;
|
|
421
|
+
flex-direction: column;
|
|
422
|
+
height: 100%;
|
|
423
|
+
min-height: 0;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/* Ensure content scrolls properly */
|
|
427
|
+
.ds-bottom-sheet ion-content {
|
|
428
|
+
--background: transparent;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/* Better mobile experience */
|
|
432
|
+
@media (max-width: 768px) {
|
|
433
|
+
.ds-bottom-sheet::part(content) {
|
|
434
|
+
max-width: 100%;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* Desktop centering */
|
|
439
|
+
@media (min-width: 769px) {
|
|
440
|
+
.ds-bottom-sheet::part(content) {
|
|
441
|
+
max-width: 640px;
|
|
442
|
+
margin: 0 auto;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/* ============================================
|
|
447
|
+
Dynamic Height Action Sheets
|
|
448
|
+
Make action sheets auto-size to their content
|
|
449
|
+
============================================ */
|
|
450
|
+
|
|
451
|
+
/* Auto-height modal - override base bottom sheet styles */
|
|
452
|
+
.ds-bottom-sheet.auto-height {
|
|
453
|
+
top: 0 !important;
|
|
454
|
+
height: 100% !important;
|
|
455
|
+
display: flex;
|
|
456
|
+
flex-direction: column;
|
|
457
|
+
justify-content: flex-end;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.ds-bottom-sheet.auto-height::part(content) {
|
|
461
|
+
position: relative !important;
|
|
462
|
+
height: auto !important;
|
|
463
|
+
max-height: 80vh !important;
|
|
464
|
+
top: auto !important;
|
|
465
|
+
bottom: auto !important;
|
|
466
|
+
margin-top: auto !important;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/* Ensure action sheet component sizes naturally */
|
|
470
|
+
.ds-bottom-sheet ds-mobile-actions-bottom-sheet {
|
|
471
|
+
display: block;
|
|
472
|
+
height: auto;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/* Ensure action list scrolls if needed */
|
|
476
|
+
.ds-bottom-sheet .actions-list {
|
|
477
|
+
max-height: calc(85dvh - 80px);
|
|
478
|
+
/* Account for handle, padding, and safe area */
|
|
479
|
+
overflow-y: auto;
|
|
480
|
+
-webkit-overflow-scrolling: touch;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/* Ensure ion-content doesn't force 100% height in sheets */
|
|
484
|
+
.ds-bottom-sheet ion-content {
|
|
485
|
+
height: auto !important;
|
|
486
|
+
--height: auto;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/* ============================================
|
|
490
|
+
Bottom Sheet Keyboard Handling
|
|
491
|
+
Uses separate --keyboard-height-sheet variable to avoid conflicts with modals.
|
|
492
|
+
The transition is always present so both keyboard-show and keyboard-hide
|
|
493
|
+
animate smoothly instead of snapping.
|
|
494
|
+
============================================ */
|
|
495
|
+
|
|
496
|
+
.ds-bottom-sheet.auto-height {
|
|
497
|
+
transition: transform 0.28s cubic-bezier(0.2, 0.8, 0.4, 1);
|
|
498
|
+
transform: translateY(calc(-1 * var(--keyboard-height-sheet, 0px)));
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/* ============================================
|
|
502
|
+
Mobile Modal Styles (Generic Modal Service)
|
|
503
|
+
============================================ */
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Mobile Modal Styles
|
|
507
|
+
*
|
|
508
|
+
* Global styles for modals opened via DsMobileModalService.
|
|
509
|
+
* These styles are applied to all modals and handle different presentation styles.
|
|
510
|
+
*/
|
|
511
|
+
|
|
512
|
+
/* Base modal styles */
|
|
513
|
+
.ds-mobile-modal {
|
|
514
|
+
--background: var(--color-background-neutral-primary, #ffffff);
|
|
515
|
+
--border-radius: 16px;
|
|
516
|
+
--box-shadow: none;
|
|
517
|
+
--max-width: 640px;
|
|
518
|
+
--width: 100%;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.ds-mobile-modal::part(content) {
|
|
522
|
+
max-width: 640px;
|
|
523
|
+
margin: 0 auto;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/* Fullscreen presentation */
|
|
527
|
+
.ds-modal-fullscreen {
|
|
528
|
+
--width: 100%;
|
|
529
|
+
--height: 100%;
|
|
530
|
+
--border-radius: 0;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.ds-modal-fullscreen::part(content) {
|
|
534
|
+
border-radius: 0;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/* Card presentation (default iOS modal style) */
|
|
538
|
+
.ds-modal-card {
|
|
539
|
+
--width: 100%;
|
|
540
|
+
--max-width: 640px;
|
|
541
|
+
--height: auto;
|
|
542
|
+
--max-height: 90%;
|
|
543
|
+
--border-radius: 16px 16px 0 0;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.ds-modal-card::part(content) {
|
|
547
|
+
border-radius: 16px 16px 0 0;
|
|
548
|
+
box-shadow: var(--box-shadow);
|
|
549
|
+
max-width: 640px;
|
|
550
|
+
margin: 0 auto;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/* Sheet presentation (bottom sheet with breakpoints) */
|
|
554
|
+
.ds-modal-sheet {
|
|
555
|
+
--width: 100%;
|
|
556
|
+
--max-width: 640px;
|
|
557
|
+
--height: auto;
|
|
558
|
+
--border-radius: 16px 16px 0 0;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.ds-modal-sheet::part(content) {
|
|
562
|
+
border-radius: 16px 16px 0 0;
|
|
563
|
+
box-shadow: var(--box-shadow);
|
|
564
|
+
max-width: 640px;
|
|
565
|
+
margin: 0 auto;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.ds-modal-sheet::part(handle) {
|
|
569
|
+
background: var(--border-color-default);
|
|
570
|
+
width: 36px;
|
|
571
|
+
height: 4px;
|
|
572
|
+
border-radius: 2px;
|
|
573
|
+
margin: 8px auto 0;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/* Backdrop styles */
|
|
577
|
+
.ds-mobile-modal::part(backdrop) {
|
|
578
|
+
background: rgba(0, 0, 0, 0.4);
|
|
579
|
+
backdrop-filter: blur(4px);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/* Animations */
|
|
583
|
+
.ds-mobile-modal.modal-card-enter-active,
|
|
584
|
+
.ds-mobile-modal.modal-sheet-enter-active {
|
|
585
|
+
animation: slideUpModal 0.5s ease-out;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.ds-mobile-modal.modal-card-leave-active,
|
|
589
|
+
.ds-mobile-modal.modal-sheet-leave-active {
|
|
590
|
+
animation: slideDownModal 0.8s ease-in;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.ds-mobile-modal.modal-fullscreen-enter-active {
|
|
594
|
+
animation: fadeInModal 0.4s ease-out;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.ds-mobile-modal.modal-fullscreen-leave-active {
|
|
598
|
+
animation: fadeOutModal 0.4s ease-in;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
@keyframes slideUpModal {
|
|
602
|
+
from {
|
|
603
|
+
transform: translateY(100%);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
to {
|
|
607
|
+
transform: translateY(0);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
@keyframes slideDownModal {
|
|
612
|
+
from {
|
|
613
|
+
transform: translateY(0);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
to {
|
|
617
|
+
transform: translateY(100%);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
@keyframes fadeInModal {
|
|
622
|
+
from {
|
|
623
|
+
opacity: 0;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
to {
|
|
627
|
+
opacity: 1;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
@keyframes fadeOutModal {
|
|
632
|
+
from {
|
|
633
|
+
opacity: 1;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
to {
|
|
637
|
+
opacity: 0;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/* Safe area handling for notched devices */
|
|
642
|
+
@supports (padding: env(safe-area-inset-top)) {
|
|
643
|
+
.ds-modal-fullscreen::part(content) {
|
|
644
|
+
padding-top: env(safe-area-inset-top);
|
|
645
|
+
padding-bottom: env(safe-area-inset-bottom);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/* Dark mode support removed - modals should not change based on system dark mode */
|
|
650
|
+
|
|
651
|
+
/* Accessibility: Reduced motion */
|
|
652
|
+
@media (prefers-reduced-motion: reduce) {
|
|
653
|
+
|
|
654
|
+
.ds-mobile-modal.modal-card-enter-active,
|
|
655
|
+
.ds-mobile-modal.modal-card-leave-active,
|
|
656
|
+
.ds-mobile-modal.modal-sheet-enter-active,
|
|
657
|
+
.ds-mobile-modal.modal-sheet-leave-active,
|
|
658
|
+
.ds-mobile-modal.modal-fullscreen-enter-active,
|
|
659
|
+
.ds-mobile-modal.modal-fullscreen-leave-active {
|
|
660
|
+
animation: none;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/* ============================================
|
|
665
|
+
ION-TAB-BUTTON CUSTOMIZATION
|
|
666
|
+
============================================ */
|
|
667
|
+
|
|
668
|
+
/* Set gap for button-inner to control spacing between icon and label */
|
|
669
|
+
ion-tab-button::part(native) {
|
|
670
|
+
gap: 2px;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/* ============================================
|
|
674
|
+
Base Modal Styles (Shared across all modals)
|
|
675
|
+
============================================ */
|
|
676
|
+
|
|
677
|
+
ion-modal.ds-modal-base {
|
|
678
|
+
--background: var(--color-background-neutral-primary, #ffffff);
|
|
679
|
+
--width: 100%;
|
|
680
|
+
--max-width: 640px;
|
|
681
|
+
--height: 100dvh;
|
|
682
|
+
/* Full viewport height - content top offset creates gap */
|
|
683
|
+
--border-radius: 16px 16px 0 0;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
ion-modal.ds-modal-base::part(content) {
|
|
687
|
+
border-radius: 16px 16px 0 0;
|
|
688
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
|
|
689
|
+
background: var(--color-background-neutral-primary, #ffffff);
|
|
690
|
+
max-width: 640px;
|
|
691
|
+
margin: 0 auto;
|
|
692
|
+
/* Use top positioning instead of margin-top to work with fixed positioning */
|
|
693
|
+
top: var(--app-sheet-top-offset) !important;
|
|
694
|
+
height: calc(100% - var(--app-sheet-top-offset)) !important;
|
|
695
|
+
max-height: calc(100dvh - var(--app-sheet-top-offset)) !important;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/* Auto-height support for base modals */
|
|
699
|
+
.ds-modal-base.auto-height {
|
|
700
|
+
display: flex;
|
|
701
|
+
flex-direction: column;
|
|
702
|
+
justify-content: flex-end;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
.ds-modal-base.auto-height::part(content) {
|
|
706
|
+
height: auto !important;
|
|
707
|
+
margin-top: auto !important;
|
|
708
|
+
position: relative !important;
|
|
709
|
+
top: auto !important;
|
|
710
|
+
max-height: calc(100dvh - var(--app-sheet-top-offset, 24px)) !important;
|
|
711
|
+
bottom: 0 !important;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
.ds-modal-base.auto-height ion-content {
|
|
715
|
+
height: auto !important;
|
|
716
|
+
--height: auto;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.ds-modal-base::part(backdrop) {
|
|
720
|
+
background: rgba(0, 0, 0, 0.4);
|
|
721
|
+
backdrop-filter: blur(4px);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
.ds-modal-base ion-content {
|
|
725
|
+
--background: var(--color-background-neutral-primary, #ffffff);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/* ============================================
|
|
729
|
+
Post Detail Modal Styles
|
|
730
|
+
============================================ */
|
|
731
|
+
|
|
732
|
+
.ds-post-detail-modal {
|
|
733
|
+
--background: var(--color-background-neutral-primary, #ffffff);
|
|
734
|
+
--width: 100%;
|
|
735
|
+
--max-width: 640px;
|
|
736
|
+
--height: 100dvh;
|
|
737
|
+
/* Full viewport height - content top offset creates gap */
|
|
738
|
+
--border-radius: 16px 16px 0 0;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.ds-post-detail-modal::part(content) {
|
|
742
|
+
border-radius: 16px 16px 0 0;
|
|
743
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
|
|
744
|
+
background: var(--color-background-neutral-primary, #ffffff);
|
|
745
|
+
max-width: 640px;
|
|
746
|
+
margin: 0 auto;
|
|
747
|
+
/* Use top positioning instead of margin-top to work with fixed positioning */
|
|
748
|
+
top: var(--app-sheet-top-offset) !important;
|
|
749
|
+
height: calc(100% - var(--app-sheet-top-offset)) !important;
|
|
750
|
+
max-height: calc(100vh - var(--app-sheet-top-offset)) !important;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
.ds-post-detail-modal::part(backdrop) {
|
|
754
|
+
background: rgba(0, 0, 0, 0.4);
|
|
755
|
+
backdrop-filter: blur(4px);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
.ds-post-detail-modal ion-content {
|
|
759
|
+
--background: var(--color-background-neutral-primary, #ffffff);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/* Control the presenting element (page behind) border radius */
|
|
763
|
+
ion-router-outlet.ion-page-hidden,
|
|
764
|
+
.ion-page.ion-page-hidden {
|
|
765
|
+
border-radius: 16px 16px 0 0 !important;
|
|
766
|
+
overflow: hidden;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/* Override Ionic's default 10px border-radius */
|
|
770
|
+
body.backdrop-no-scroll ion-router-outlet {
|
|
771
|
+
border-radius: 16px 16px 0 0 !important;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/* Target the router outlet when modal is active */
|
|
775
|
+
ion-router-outlet:not(.ion-page-hidden) {
|
|
776
|
+
border-radius: 16px 16px 0 0 !important;
|
|
777
|
+
transition: border-radius 0.3s ease, transform 0.3s ease;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/* Adjust the presenting element animation */
|
|
781
|
+
.modal-card-enter-active ion-router-outlet,
|
|
782
|
+
.modal-card-enter-active .ion-page {
|
|
783
|
+
border-radius: 16px 16px 0 0 !important;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/* When page is scaled behind modal */
|
|
787
|
+
.ion-page-invisible {
|
|
788
|
+
border-radius: 16px 16px 0 0 !important;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/* Override Ionic's default presenting page styles */
|
|
792
|
+
.modal-shadow .ion-page {
|
|
793
|
+
border-radius: 16px 16px 0 0 !important;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/* ============================================
|
|
797
|
+
More Menu Sheet
|
|
798
|
+
Mobile: sits above the bottom tab bar, clipped so
|
|
799
|
+
backdrop doesn't bleed over it.
|
|
800
|
+
Desktop: behaves like a normal bottom sheet (tab bar
|
|
801
|
+
is at the top, no special handling needed).
|
|
802
|
+
============================================ */
|
|
803
|
+
|
|
804
|
+
@media (max-width: 767px) {
|
|
805
|
+
.ds-more-menu-sheet {
|
|
806
|
+
z-index: 100 !important;
|
|
807
|
+
bottom: var(--ds-more-menu-tab-bar-offset, 64px) !important;
|
|
808
|
+
height: calc(100% - var(--ds-more-menu-tab-bar-offset, 64px)) !important;
|
|
809
|
+
overflow: hidden;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
.ds-more-menu-sheet.auto-height {
|
|
813
|
+
padding-bottom: 0;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
.ds-more-menu-sheet .bottom-sheet-wrapper {
|
|
818
|
+
min-height: 0 !important;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/* Hide modal shadow when auto-height sheet is active */
|
|
822
|
+
body:has(.ds-bottom-sheet.auto-height) .modal-shadow {
|
|
823
|
+
display: none !important;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
ion-modal.show-modal ~ ion-router-outlet,
|
|
827
|
+
ion-modal.show-modal ~ * ion-router-outlet {
|
|
828
|
+
border-radius: 16px 16px 0 0 !important;
|
|
829
|
+
transform: scale(0.915) !important;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/* Most important - override any Ionic defaults */
|
|
833
|
+
ion-router-outlet[style*="border-radius"] {
|
|
834
|
+
border-radius: 16px 16px 0 0 !important;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/* Target app root with modal */
|
|
838
|
+
ion-app.ion-page-hidden ion-router-outlet,
|
|
839
|
+
ion-app ion-router-outlet.ion-page-hidden {
|
|
840
|
+
border-radius: 16px 16px 0 0 !important;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/* Safe area handling - NOT needed here since modal positioning (--app-sheet-top-offset) handles it */
|
|
844
|
+
|
|
845
|
+
/* Dark mode support removed - modals should not change based on system dark mode */
|
|
846
|
+
|
|
847
|
+
/* ============================================
|
|
848
|
+
Chat Modal Styles
|
|
849
|
+
============================================ */
|
|
850
|
+
|
|
851
|
+
.ds-chat-modal {
|
|
852
|
+
--background: var(--color-background-neutral-primary, #ffffff);
|
|
853
|
+
--width: 100%;
|
|
854
|
+
--max-width: 640px;
|
|
855
|
+
--height: 100dvh;
|
|
856
|
+
/* Full viewport height - content top offset creates gap */
|
|
857
|
+
--border-radius: 16px 16px 0 0;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
.ds-chat-modal::part(content) {
|
|
861
|
+
border-radius: 16px 16px 0 0;
|
|
862
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
|
|
863
|
+
background: var(--color-background-neutral-primary, #ffffff);
|
|
864
|
+
max-width: 640px;
|
|
865
|
+
margin: 0 auto;
|
|
866
|
+
/* Use top positioning instead of margin-top to work with fixed positioning */
|
|
867
|
+
top: var(--app-sheet-top-offset) !important;
|
|
868
|
+
height: calc(100% - var(--app-sheet-top-offset)) !important;
|
|
869
|
+
max-height: calc(100vh - var(--app-sheet-top-offset)) !important;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
.ds-chat-modal::part(backdrop) {
|
|
873
|
+
background: rgba(0, 0, 0, 0.4);
|
|
874
|
+
backdrop-filter: blur(4px);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
.ds-chat-modal ion-content {
|
|
878
|
+
--background: var(--color-background-neutral-primary, #ffffff);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/* Dark mode support removed - modals should not change based on system dark mode */
|
|
882
|
+
|
|
883
|
+
/* ============================================
|
|
884
|
+
Handbook Detail Modal Styles
|
|
885
|
+
============================================ */
|
|
886
|
+
|
|
887
|
+
.ds-handbook-detail-modal {
|
|
888
|
+
--background: var(--color-background-neutral-primary, #ffffff);
|
|
889
|
+
--width: 100%;
|
|
890
|
+
--max-width: 640px;
|
|
891
|
+
--height: 100dvh;
|
|
892
|
+
/* Use dynamic viewport height instead of 100% */
|
|
893
|
+
--border-radius: 16px 16px 0 0;
|
|
894
|
+
margin-top: var(--app-sheet-top-offset);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
.ds-handbook-detail-modal::part(content) {
|
|
898
|
+
border-radius: 16px 16px 0 0;
|
|
899
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
|
|
900
|
+
background: var(--color-background-neutral-primary, #ffffff);
|
|
901
|
+
max-width: 640px;
|
|
902
|
+
margin: 0 auto;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
.ds-handbook-detail-modal::part(backdrop) {
|
|
906
|
+
background: rgba(0, 0, 0, 0.4);
|
|
907
|
+
backdrop-filter: blur(4px);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
.ds-handbook-detail-modal ion-content {
|
|
911
|
+
--background: var(--color-background-neutral-primary, #ffffff);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
/* Safe area handling - NOT needed here since modal positioning (--app-sheet-top-offset) handles it */
|
|
915
|
+
|
|
916
|
+
/* Dark mode support */
|
|
917
|
+
@media (prefers-color-scheme: dark) {
|
|
918
|
+
.ds-handbook-detail-modal {
|
|
919
|
+
--background: var(--color-background-neutral-primary-dark, #1a1a1a);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
.ds-handbook-detail-modal::part(content) {
|
|
923
|
+
background: var(--color-background-neutral-primary-dark, #1a1a1a);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
.ds-handbook-detail-modal::part(backdrop) {
|
|
927
|
+
background: rgba(0, 0, 0, 0.6);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
.ds-handbook-detail-modal ion-content {
|
|
931
|
+
--background: var(--color-background-neutral-primary-dark, #1a1a1a);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/* ============================================
|
|
936
|
+
Whitelabel Demo Modal Styles
|
|
937
|
+
============================================ */
|
|
938
|
+
|
|
939
|
+
.ds-whitelabel-demo-modal {
|
|
940
|
+
--background: var(--color-background-neutral-primary, #ffffff);
|
|
941
|
+
--width: 100%;
|
|
942
|
+
--max-width: 640px;
|
|
943
|
+
--height: 100dvh;
|
|
944
|
+
/* Full viewport height - content top offset creates gap */
|
|
945
|
+
--border-radius: 16px 16px 0 0;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
.ds-whitelabel-demo-modal::part(content) {
|
|
949
|
+
border-radius: 16px 16px 0 0;
|
|
950
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
|
|
951
|
+
background: var(--color-background-neutral-primary, #ffffff);
|
|
952
|
+
max-width: 640px;
|
|
953
|
+
margin: 0 auto;
|
|
954
|
+
/* Use top positioning instead of margin-top to work with fixed positioning */
|
|
955
|
+
top: var(--app-sheet-top-offset) !important;
|
|
956
|
+
height: calc(100% - var(--app-sheet-top-offset)) !important;
|
|
957
|
+
max-height: calc(100vh - var(--app-sheet-top-offset)) !important;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
.ds-whitelabel-demo-modal::part(backdrop) {
|
|
961
|
+
background: rgba(0, 0, 0, 0.4);
|
|
962
|
+
backdrop-filter: blur(4px);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.ds-whitelabel-demo-modal ion-content {
|
|
966
|
+
--background: #ffffff;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
/* Dark mode support removed - modals should not change based on system dark mode */
|
|
970
|
+
|
|
971
|
+
/* ============================================
|
|
972
|
+
GLOBAL BUTTON INTERACTIVE STATES
|
|
973
|
+
Consistent button behavior across all ds-button instances
|
|
974
|
+
============================================ */
|
|
975
|
+
|
|
976
|
+
/* Base transition */
|
|
977
|
+
ds-button .btn {
|
|
978
|
+
transition: all 0.2s ease !important;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/* Active/pressed/click state - scale down slightly */
|
|
982
|
+
ds-button .btn:active:not(:disabled) {
|
|
983
|
+
transform: scale(0.98) !important;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/* FORCE hover background color directly - only for primary variant */
|
|
987
|
+
ds-button[variant="primary"]:hover .btn {
|
|
988
|
+
background-color: var(--color-accent-hover) !important;
|
|
989
|
+
}
|