Skip to content

Commit 719ef75

Browse files
authored
experiment: add master-detail-layout base styles and visual tests (#9849)
1 parent d66081f commit 719ef75

22 files changed

+416
-38
lines changed

dev/master-detail-layout.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
<meta charset="UTF-8" />
55
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7-
<title>Master Detail Layout</title>
7+
<title>Master-Detail Layout</title>
8+
<link rel="stylesheet" href="/packages/vaadin-lumo-styles/lumo.css" />
89
<script type="module" src="./common.js"></script>
910
</head>
1011

packages/master-detail-layout/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
"type": "module",
2222
"files": [
2323
"src",
24+
"!src/styles/*-base-styles.d.ts",
25+
"!src/styles/*-base-styles.js",
2426
"theme",
2527
"vaadin-*.d.ts",
2628
"vaadin-*.js",
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2025 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import type { CSSResult } from 'lit';
7+
8+
export const masterDetailLayoutStyles: CSSResult;
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2025 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import '@vaadin/component-base/src/style-props.js';
7+
import { css } from 'lit';
8+
9+
export const masterDetailLayoutStyles = css`
10+
/* Layout and positioning styles */
11+
12+
:host {
13+
display: flex;
14+
box-sizing: border-box;
15+
height: 100%;
16+
max-width: 100%;
17+
max-height: 100%;
18+
position: relative; /* Keep the positioning context stable across all modes */
19+
overflow: hidden;
20+
}
21+
22+
:host([hidden]) {
23+
display: none !important;
24+
}
25+
26+
:host([orientation='vertical']) {
27+
flex-direction: column;
28+
}
29+
30+
[part='_detail-internal'] {
31+
display: contents;
32+
justify-content: end;
33+
}
34+
35+
:host([orientation='vertical']) [part='_detail-internal'] {
36+
align-items: end;
37+
}
38+
39+
:host(:is([drawer], [stack])) [part='_detail-internal'],
40+
:host(:is([drawer], [stack])[has-detail]) [part='backdrop'] {
41+
display: flex;
42+
position: absolute;
43+
z-index: 1;
44+
inset: 0;
45+
overscroll-behavior: contain;
46+
}
47+
48+
:host(:not([has-detail])) [part='_detail-internal'],
49+
[part='backdrop'] {
50+
display: none;
51+
}
52+
53+
:host([orientation='horizontal'][drawer]) [part='detail'] {
54+
margin-inline-start: 50px;
55+
}
56+
57+
:host([orientation='vertical'][drawer]) [part='detail'] {
58+
margin-top: 50px;
59+
}
60+
61+
:host(:is([drawer], [stack])[containment='viewport']) :is([part='_detail-internal'], [part='backdrop']) {
62+
position: fixed;
63+
}
64+
65+
/* Sizing styles */
66+
67+
[part] {
68+
box-sizing: border-box;
69+
max-width: 100%;
70+
max-height: 100%;
71+
}
72+
73+
/* No fixed size */
74+
:host(:not([has-master-size])) [part='master'],
75+
:host(:not([has-detail-size]):not([drawer], [stack])) [part='detail'] {
76+
flex-grow: 1;
77+
flex-basis: 50%;
78+
}
79+
80+
/* Fixed size */
81+
:host([has-master-size]) [part='master'],
82+
:host([has-detail-size]) [part='detail'] {
83+
flex-shrink: 0;
84+
}
85+
86+
:host([orientation='horizontal'][has-master-size][has-detail]) [part='master'] {
87+
width: var(--_master-size);
88+
}
89+
90+
:host([orientation='vertical'][has-master-size][has-detail]) [part='master'] {
91+
height: var(--_master-size);
92+
}
93+
94+
:host([orientation='horizontal'][has-detail-size]:not([stack])) [part='detail'] {
95+
width: var(--_detail-size);
96+
}
97+
98+
:host([orientation='vertical'][has-detail-size]:not([stack])) [part='detail'] {
99+
height: var(--_detail-size);
100+
}
101+
102+
:host([has-master-size][has-detail-size]) [part='master'] {
103+
flex-grow: 1;
104+
flex-basis: var(--_master-size);
105+
}
106+
107+
:host([has-master-size][has-detail-size]:not([drawer], [stack])) [part='detail'] {
108+
flex-grow: 1;
109+
flex-basis: var(--_detail-size);
110+
}
111+
112+
/* Min size */
113+
:host([orientation='horizontal'][has-master-min-size]) [part='master'] {
114+
min-width: min(100%, var(--_master-min-size));
115+
}
116+
117+
:host([orientation='vertical'][has-master-min-size]) [part='master'] {
118+
min-height: min(100%, var(--_master-min-size));
119+
}
120+
121+
:host([orientation='horizontal'][has-detail-min-size]) [part='detail'] {
122+
min-width: min(100%, var(--_detail-min-size));
123+
}
124+
125+
:host([orientation='vertical'][has-detail-min-size]) [part='detail'] {
126+
min-height: min(100%, var(--_detail-min-size));
127+
}
128+
129+
:host([drawer]) [part='master'],
130+
:host([stack]) [part] {
131+
width: 100% !important;
132+
height: 100% !important;
133+
min-width: auto !important;
134+
min-height: auto !important;
135+
max-width: 100% !important;
136+
max-height: 100% !important;
137+
}
138+
139+
/* Decorative/visual styles */
140+
141+
[part='backdrop'] {
142+
background: var(--vaadin-master-detail-layout-backdrop, rgba(0, 0, 0, 0.2));
143+
}
144+
145+
:host(:is([drawer], [stack])) [part='detail'] {
146+
background: var(--vaadin-master-detail-layout-detail-background, var(--vaadin-background-color));
147+
box-shadow: var(--vaadin-master-detail-layout-detail-shadow, 0 0 20px 0 rgba(0, 0, 0, 0.3));
148+
}
149+
150+
:host([orientation='horizontal']:not([drawer], [stack])) [part='detail'] {
151+
border-inline-start: var(--vaadin-master-detail-layout-border-width, 1px) solid
152+
var(--vaadin-master-detail-layout-border-color, var(--vaadin-border-color));
153+
}
154+
155+
:host([orientation='vertical']:not([drawer], [stack])) [part='detail'] {
156+
border-top: var(--vaadin-master-detail-layout-border-width, 1px) solid
157+
var(--vaadin-master-detail-layout-border-color, var(--vaadin-border-color));
158+
}
159+
160+
@media (forced-colors: active) {
161+
:host(:is([drawer], [stack])) [part='detail'] {
162+
outline: 3px solid;
163+
}
164+
}
165+
`;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2025 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import type { CSSResult } from 'lit';
7+
8+
export const masterDetailLayoutTransitionStyles: CSSResult;
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2025 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import { css } from 'lit';
7+
8+
export const masterDetailLayoutTransitionStyles = css`
9+
@media (prefers-reduced-motion: no-preference) {
10+
html {
11+
--_vaadin-mdl-dir-multiplier: 1;
12+
--_vaadin-mdl-stack-master-offset: 20%;
13+
--_vaadin-mdl-stack-master-clip-path: inset(0 0 0 var(--_vaadin-mdl-stack-master-offset));
14+
--_vaadin-mdl-easing: cubic-bezier(0.78, 0, 0.22, 1);
15+
}
16+
17+
html[dir='rtl'] {
18+
--_vaadin-mdl-dir-multiplier: -1;
19+
--_vaadin-mdl-stack-master-clip-path: inset(0 var(--_vaadin-mdl-stack-master-offset) 0 0);
20+
}
21+
22+
::view-transition-group(vaadin-mdl-backdrop),
23+
::view-transition-group(vaadin-mdl-master),
24+
::view-transition-group(vaadin-mdl-detail) {
25+
animation-duration: 0.4s;
26+
}
27+
28+
::view-transition-group(vaadin-mdl-master),
29+
::view-transition-group(vaadin-mdl-detail) {
30+
animation-timing-function: var(--_vaadin-mdl-easing);
31+
}
32+
33+
::view-transition-image-pair(vaadin-mdl-master),
34+
::view-transition-image-pair(vaadin-mdl-detail),
35+
::view-transition-new(vaadin-mdl-master),
36+
::view-transition-new(vaadin-mdl-detail),
37+
::view-transition-old(vaadin-mdl-master),
38+
::view-transition-old(vaadin-mdl-detail) {
39+
animation-timing-function: inherit;
40+
}
41+
42+
/* Needed to promote the backdrop on top the master during the transition */
43+
vaadin-master-detail-layout[transition]::part(backdrop) {
44+
view-transition-name: vaadin-mdl-backdrop;
45+
}
46+
47+
vaadin-master-detail-layout[transition]:not([transition='replace']):not([drawer], [stack])::part(detail),
48+
vaadin-master-detail-layout[transition]:is([drawer], [stack])::part(_detail-internal) {
49+
view-transition-name: vaadin-mdl-detail;
50+
}
51+
52+
::view-transition-group(vaadin-mdl-detail) {
53+
clip-path: inset(0);
54+
}
55+
56+
::view-transition-new(vaadin-mdl-detail),
57+
::view-transition-old(vaadin-mdl-detail) {
58+
animation-name: vaadin-mdl-detail-slide-in;
59+
}
60+
61+
::view-transition-old(vaadin-mdl-detail) {
62+
animation-direction: reverse;
63+
}
64+
65+
@keyframes vaadin-mdl-detail-slide-in {
66+
0% {
67+
translate: calc((100% + 30px) * var(--_vaadin-mdl-dir-multiplier));
68+
}
69+
}
70+
71+
vaadin-master-detail-layout[orientation='horizontal'][stack][has-detail]::part(master) {
72+
translate: calc(var(--_vaadin-mdl-stack-master-offset) * var(--_vaadin-mdl-dir-multiplier) * -1);
73+
opacity: 0;
74+
}
75+
76+
vaadin-master-detail-layout[transition]::part(master) {
77+
view-transition-name: vaadin-mdl-master;
78+
}
79+
80+
vaadin-master-detail-layout[orientation='horizontal'][stack][transition='add']::part(master) {
81+
view-transition-class: stack-add;
82+
}
83+
84+
vaadin-master-detail-layout[orientation='horizontal'][stack][transition='remove']::part(master) {
85+
view-transition-class: stack-remove;
86+
}
87+
88+
::view-transition-new(vaadin-mdl-master),
89+
::view-transition-old(vaadin-mdl-master) {
90+
object-fit: none;
91+
object-position: max(0%, var(--_vaadin-mdl-dir-multiplier) * -100%) 0;
92+
}
93+
94+
::view-transition-new(vaadin-mdl-master.stack-remove),
95+
::view-transition-old(vaadin-mdl-master.stack-remove) {
96+
animation-name: vaadin-mdl-master-stack-remove;
97+
clip-path: var(--_vaadin-mdl-stack-master-clip-path);
98+
}
99+
100+
@keyframes vaadin-mdl-master-stack-remove {
101+
100% {
102+
clip-path: inset(0);
103+
}
104+
}
105+
106+
::view-transition-new(vaadin-mdl-master.stack-add),
107+
::view-transition-old(vaadin-mdl-master.stack-add) {
108+
animation-name: vaadin-mdl-master-stack-add;
109+
clip-path: inset(0);
110+
}
111+
112+
@keyframes vaadin-mdl-master-stack-add {
113+
100% {
114+
clip-path: var(--_vaadin-mdl-stack-master-clip-path);
115+
}
116+
}
117+
118+
/* prettier-ignore */
119+
vaadin-master-detail-layout[orientation='vertical']:not([drawer], [stack])[transition]:not([transition='replace'])::part(detail),
120+
vaadin-master-detail-layout[orientation='vertical']:is([drawer], [stack])[transition]::part(_detail-internal) {
121+
view-transition-name: vaadin-mdl-detail;
122+
view-transition-class: vertical;
123+
}
124+
125+
::view-transition-new(vaadin-mdl-detail.vertical),
126+
::view-transition-old(vaadin-mdl-detail.vertical) {
127+
animation-name: vaadin-mdl-vertical-detail-slide-in;
128+
}
129+
130+
::view-transition-old(vaadin-mdl-detail.vertical) {
131+
animation-direction: reverse;
132+
}
133+
134+
@keyframes vaadin-mdl-vertical-detail-slide-in {
135+
0% {
136+
transform: translateY(calc(100% + 30px));
137+
}
138+
}
139+
}
140+
`;

packages/master-detail-layout/src/styles/vaadin-master-detail-layout-transition-core-styles.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const masterDetailLayoutTransitionStyles = css`
4040
}
4141
4242
/* Needed to promote the backdrop on top the master during the transition */
43-
vaadin-master-detail-layout[transition]:not([transition='replace'])::part(backdrop) {
43+
vaadin-master-detail-layout[transition]::part(backdrop) {
4444
view-transition-name: vaadin-mdl-backdrop;
4545
}
4646

packages/master-detail-layout/src/vaadin-master-detail-layout.js

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
1010
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
1111
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
1212
import { SlotStylesMixin } from '@vaadin/component-base/src/slot-styles-mixin.js';
13-
import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
1413
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
1514
import { masterDetailLayoutStyles } from './styles/vaadin-master-detail-layout-core-styles.js';
1615
import { masterDetailLayoutTransitionStyles } from './styles/vaadin-master-detail-layout-transition-core-styles.js';
@@ -52,9 +51,7 @@ import { masterDetailLayoutTransitionStyles } from './styles/vaadin-master-detai
5251
* @mixes ResizeMixin
5352
* @mixes SlotStylesMixin
5453
*/
55-
class MasterDetailLayout extends SlotStylesMixin(
56-
ResizeMixin(ElementMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))))),
57-
) {
54+
class MasterDetailLayout extends SlotStylesMixin(ResizeMixin(ElementMixin(ThemableMixin(PolylitMixin(LitElement))))) {
5855
static get is() {
5956
return 'vaadin-master-detail-layout';
6057
}
@@ -63,12 +60,6 @@ class MasterDetailLayout extends SlotStylesMixin(
6360
return masterDetailLayoutStyles;
6461
}
6562

66-
static get lumoInjector() {
67-
return {
68-
includeBaseStyles: true,
69-
};
70-
}
71-
7263
static get properties() {
7364
return {
7465
/**

0 commit comments

Comments
 (0)