Skip to content

Commit ffe251a

Browse files
authored
experiment: add app-layout base styles and visual tests (#9269)
1 parent 8d8302b commit ffe251a

26 files changed

+374
-27
lines changed

dev/app-layout.html

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
<!DOCTYPE html>
1+
<!doctype html>
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
55
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
7+
<meta name="mobile-web-app-capable" content="yes" />
8+
<meta name="apple-mobile-web-app-capable" content="yes" />
9+
<!-- possible content values: default, black or black-translucent -->
10+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
711
<title>App Layout</title>
812
<script type="module" src="./common.js"></script>
913

@@ -12,41 +16,83 @@
1216
margin: 0;
1317
}
1418

15-
header h1,
16-
h2[slot~="navbar"] {
17-
font-size: var(--lumo-font-size-l);
19+
header div,
20+
div[slot~='navbar'] {
21+
font-size: 1.125rem;
1822
margin: 0;
23+
color: var(--_vaadin-color-strong);
24+
font-weight: 600;
1925
}
2026

2127
:is(header, footer) {
2228
display: flex;
2329
align-items: center;
24-
gap: var(--lumo-space-s);
25-
padding: var(--lumo-space-s) var(--lumo-space-m);
26-
min-height: var(--lumo-size-xl);
30+
gap: 0.5rem;
31+
padding: 0.5rem 1rem;
32+
min-height: 3rem;
2733
box-sizing: border-box;
2834
}
2935

3036
dummy-content {
31-
display: block;
32-
background-image: linear-gradient(var(--lumo-primary-color), var(--lumo-primary-color-10pct));
33-
border-radius: var(--lumo-border-radius-l);
34-
margin: var(--lumo-space-m);
37+
display: flex;
38+
place-items: center;
39+
place-content: center;
40+
background-image: linear-gradient(45deg, hsl(0 0 50), hsl(0 0 90));
41+
border: 2px solid;
42+
border-radius: 0.5rem;
43+
margin: 1rem;
44+
padding: 1rem;
3545
height: 150vh;
3646
}
47+
48+
vaadin-app-layout {
49+
--vaadin-app-layout-drawer-width: auto;
50+
}
51+
52+
vaadin-app-layout:not([overlay]) vaadin-scroller dummy-content {
53+
width: 12rem;
54+
transition: width 120ms;
55+
margin-block: 0;
56+
}
57+
58+
.small-drawer:not([overlay]) vaadin-scroller dummy-content {
59+
width: 5rem;
60+
}
61+
62+
.small-drawer:not([overlay]) vaadin-button {
63+
scale: -1 1;
64+
}
65+
66+
[overlay] vaadin-button {
67+
display: none;
68+
}
69+
70+
dummy-content::before {
71+
content: 'Content';
72+
}
3773
</style>
3874

3975
<script type="module">
4076
import '@vaadin/app-layout';
41-
import '@vaadin/app-layout/vaadin-drawer-toggle';
77+
import '@vaadin/app-layout/vaadin-drawer-toggle.js';
4278
import '@vaadin/scroller';
79+
import '@vaadin/button';
80+
import '@vaadin/icon';
81+
import '@vaadin/icons';
82+
83+
document.querySelector('vaadin-button').addEventListener('click', () => {
84+
document.querySelector('vaadin-app-layout').classList.toggle('small-drawer');
85+
});
4386
</script>
4487
</head>
4588

4689
<body>
47-
<vaadin-app-layout primary-section="drawer">
90+
<vaadin-app-layout primary-section="navbar">
4891
<header slot="drawer">
49-
<h1>My App</h1>
92+
<div>Drawer</div>
93+
<vaadin-button>
94+
<vaadin-icon icon="vaadin:caret-square-left-o"></vaadin-icon>
95+
</vaadin-button>
5096
</header>
5197
<vaadin-scroller slot="drawer" theme="overflow-indicators">
5298
<dummy-content></dummy-content>
@@ -56,10 +102,9 @@ <h1>My App</h1>
56102
</footer>
57103

58104
<vaadin-drawer-toggle slot="navbar touch-optimized"></vaadin-drawer-toggle>
59-
<h2 slot="navbar touch-optimized">View name</h2>
105+
<div slot="navbar touch-optimized">Navbar</div>
60106

61107
<dummy-content></dummy-content>
62-
63108
</vaadin-app-layout>
64109
</body>
65110
</html>

packages/app-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",
File renamed without changes.
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2018 - 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 appLayoutStyles = css`
10+
@layer base {
11+
:host {
12+
display: block;
13+
box-sizing: border-box;
14+
height: 100%;
15+
--vaadin-app-layout-transition-duration: 0s;
16+
transition: padding var(--vaadin-app-layout-transition-duration);
17+
--_vaadin-app-layout-drawer-width: var(--vaadin-app-layout-drawer-width, auto);
18+
--vaadin-app-layout-touch-optimized: false;
19+
--vaadin-app-layout-navbar-offset-top: var(--_vaadin-app-layout-navbar-offset-size);
20+
--vaadin-app-layout-navbar-offset-bottom: var(--_vaadin-app-layout-navbar-offset-size-bottom);
21+
padding-block: var(--vaadin-app-layout-navbar-offset-top) var(--vaadin-app-layout-navbar-offset-bottom);
22+
padding-inline-start: var(--vaadin-app-layout-navbar-offset-left);
23+
}
24+
25+
:host([hidden]),
26+
[hidden] {
27+
display: none !important;
28+
}
29+
30+
@media (prefers-reduced-motion: no-preference) {
31+
:host(:not([no-anim])) {
32+
--vaadin-app-layout-transition-duration: 200ms;
33+
}
34+
}
35+
36+
:host([drawer-opened]) {
37+
--vaadin-app-layout-drawer-offset-left: var(--_vaadin-app-layout-drawer-offset-size);
38+
}
39+
40+
:host([overlay]) {
41+
--vaadin-app-layout-drawer-offset-left: 0;
42+
--vaadin-app-layout-navbar-offset-left: 0;
43+
}
44+
45+
:host(:not([no-scroll])) [content] {
46+
overflow: auto;
47+
}
48+
49+
[content] {
50+
height: 100%;
51+
}
52+
53+
@media (pointer: coarse) and (max-width: 800px) and (min-height: 500px) {
54+
:host {
55+
--vaadin-app-layout-touch-optimized: true;
56+
}
57+
}
58+
59+
[part='navbar'] {
60+
position: fixed;
61+
display: flex;
62+
align-items: center;
63+
top: 0;
64+
inset-inline: 0;
65+
transition: inset-inline-start var(--vaadin-app-layout-transition-duration);
66+
padding-top: max(var(--vaadin-app-layout-navbar-padding-top, var(--_vaadin-padding)), var(--safe-area-inset-top));
67+
padding-bottom: var(--vaadin-app-layout-navbar-padding-bottom, var(--_vaadin-padding));
68+
padding-inline-start: max(
69+
var(--vaadin-app-layout-navbar-padding-inline-start, var(--_vaadin-padding)),
70+
var(--safe-area-inset-left)
71+
);
72+
/* stylelint-disable-next-line declaration-block-no-redundant-longhand-properties */
73+
padding-inline-end: max(
74+
var(--vaadin-app-layout-navbar-padding-inline-end, var(--_vaadin-padding)),
75+
var(--safe-area-inset-right)
76+
);
77+
z-index: 1;
78+
gap: var(--vaadin-app-layout-navbar-gap, var(--_vaadin-gap-container-inline));
79+
background: var(--vaadin-app-layout-navbar-background, var(--_vaadin-background-container));
80+
}
81+
82+
:host([primary-section='drawer'][drawer-opened]:not([overlay])) [part='navbar'] {
83+
inset-inline-start: var(--vaadin-app-layout-drawer-offset-left, 0);
84+
}
85+
86+
:host([primary-section='drawer']) [part='drawer'] {
87+
top: 0;
88+
}
89+
90+
[part='navbar'][bottom] {
91+
top: auto;
92+
bottom: 0;
93+
padding-top: var(--vaadin-app-layout-navbar-padding-top, var(--_vaadin-padding));
94+
padding-bottom: max(
95+
var(--vaadin-app-layout-navbar-padding-bottom, var(--_vaadin-padding)),
96+
var(--safe-area-inset-bottom)
97+
);
98+
}
99+
100+
[part='drawer'] {
101+
overflow: auto;
102+
overscroll-behavior: contain;
103+
position: fixed;
104+
top: var(--vaadin-app-layout-navbar-offset-top, 0);
105+
bottom: var(--vaadin-app-layout-navbar-offset-bottom, var(--vaadin-viewport-offset-bottom, 0));
106+
inset-inline: var(--vaadin-app-layout-navbar-offset-left, 0) auto;
107+
transition:
108+
transform var(--vaadin-app-layout-transition-duration),
109+
visibility var(--vaadin-app-layout-transition-duration);
110+
transform: translateX(-100%);
111+
max-width: 90%;
112+
width: var(--_vaadin-app-layout-drawer-width);
113+
box-sizing: border-box;
114+
padding: var(--safe-area-inset-top) 0 var(--safe-area-inset-bottom) var(--safe-area-inset-left);
115+
outline: none;
116+
/* The drawer should be inaccessible by the tabbing navigation when it is closed. */
117+
visibility: hidden;
118+
display: flex;
119+
flex-direction: column;
120+
background: var(--vaadin-app-layout-drawer-background, var(--_vaadin-background));
121+
}
122+
123+
:host([drawer-opened]) [part='drawer'] {
124+
/* The drawer should be accessible by the tabbing navigation when it is opened. */
125+
visibility: visible;
126+
transform: translateX(0%);
127+
touch-action: manipulation;
128+
}
129+
130+
[part='backdrop'] {
131+
background: var(--vaadin-overlay-backdrop-background, rgba(0, 0, 0, 0.5));
132+
}
133+
134+
:host(:not([drawer-opened])) [part='backdrop'] {
135+
opacity: 0 !important;
136+
}
137+
138+
:host([overlay]) [part='backdrop'] {
139+
position: fixed;
140+
inset: 0;
141+
pointer-events: none;
142+
transition: opacity var(--vaadin-app-layout-transition-duration);
143+
-webkit-tap-highlight-color: transparent;
144+
}
145+
146+
:host([overlay]) [part='drawer'] {
147+
top: 0;
148+
bottom: 0;
149+
box-shadow: var(--vaadin-overlay-box-shadow, 0 8px 24px -4px hsl(0 0 0 / 0.3));
150+
}
151+
152+
:host([overlay]) [part='drawer'],
153+
:host([overlay]) [part='backdrop'] {
154+
z-index: 2;
155+
}
156+
157+
:host([drawer-opened][overlay]) [part='backdrop'] {
158+
pointer-events: auto;
159+
touch-action: manipulation;
160+
}
161+
162+
:host([dir='rtl']) [part='drawer'] {
163+
transform: translateX(100%);
164+
}
165+
166+
:host([dir='rtl'][drawer-opened]) [part='drawer'] {
167+
transform: translateX(0%);
168+
}
169+
170+
:host([drawer-opened]:not([overlay])) {
171+
padding-inline-start: var(--vaadin-app-layout-drawer-offset-left);
172+
}
173+
174+
@media (max-width: 800px), (max-height: 600px) {
175+
:host {
176+
--vaadin-app-layout-drawer-overlay: true;
177+
--_vaadin-app-layout-drawer-width: var(--vaadin-app-layout-drawer-width, 20em);
178+
}
179+
}
180+
181+
/* If a vaadin-scroller is used in the drawer, allow it to take all remaining space and contain scrolling */
182+
[part='drawer'] ::slotted(vaadin-scroller) {
183+
flex: 1;
184+
overscroll-behavior: contain;
185+
}
186+
187+
@media (forced-colors: active) {
188+
:host([overlay]) [part='drawer'] {
189+
border: 3px solid;
190+
}
191+
}
192+
}
193+
`;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2018 - 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 appLayoutStyles: CSSResult;

packages/app-layout/src/vaadin-app-layout-styles.js renamed to packages/app-layout/src/styles/vaadin-app-layout-core-styles.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Copyright (c) 2018 - 2025 Vaadin Ltd.
44
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
55
*/
6-
import { css } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
6+
import { css } from 'lit';
77

88
export const appLayoutStyles = css`
99
:host {
@@ -26,7 +26,7 @@ export const appLayoutStyles = css`
2626
}
2727
2828
:host([no-anim]) {
29-
--vaadin-app-layout-transition-duration: none !important;
29+
--vaadin-app-layout-transition-duration: 0s !important;
3030
}
3131
3232
:host([drawer-opened]) {
File renamed without changes.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2018 - 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 drawerToggle = css`
10+
@layer base {
11+
[part='icon'] {
12+
background: currentColor;
13+
display: block;
14+
height: var(--vaadin-icon-size, 1lh);
15+
mask-image: var(--_vaadin-icon-menu);
16+
width: var(--vaadin-icon-size, 1lh);
17+
}
18+
19+
[hidden] {
20+
display: none !important;
21+
}
22+
23+
@media (forced-colors: active) {
24+
[part='icon'] {
25+
background: CanvasText;
26+
}
27+
}
28+
}
29+
`;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2018 - 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 drawerToggle: CSSResult;

0 commit comments

Comments
 (0)