Skip to content

Commit 74f3940

Browse files
authored
experiment: add tabs base styles and visual tests (#9481)
1 parent 2d08b72 commit 74f3940

29 files changed

+548
-64
lines changed

dev/tabs.html

Lines changed: 107 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,117 @@
99
<script type="module" src="./common.js"></script>
1010

1111
<script type="module">
12+
import '@vaadin/checkbox-group';
13+
import '@vaadin/checkbox';
14+
import '@vaadin/icon';
15+
import '@vaadin/icons';
1216
import '@vaadin/tabs';
13-
import '@vaadin/tooltip';
17+
18+
const icons = [
19+
'vaadin:home',
20+
'vaadin:folder-open',
21+
'vaadin:chart',
22+
'vaadin:bed',
23+
'vaadin:boat',
24+
'vaadin:calendar',
25+
'vaadin:chat',
26+
'vaadin:cube',
27+
'vaadin:file-o',
28+
'vaadin:laptop',
29+
'vaadin:picture',
30+
];
31+
32+
function addIcon(item) {
33+
const icon = document.createElement('vaadin-icon');
34+
icon.icon = icons[Math.floor(Math.random() * icons.length)];
35+
item.prepend(icon);
36+
}
37+
38+
const tabs = document.querySelector('vaadin-tabs');
39+
40+
document.querySelector('vaadin-checkbox-group').addEventListener('change', (e) => {
41+
if (e.target.value == 'vertical') {
42+
tabs.orientation = e.target.checked ? 'vertical' : 'horizontal';
43+
}
44+
45+
if (e.target.value == 'size') {
46+
tabs.classList.toggle('size', e.target.checked);
47+
}
48+
49+
if (e.target.value == 'hide-scroll-buttons') {
50+
tabs.setAttribute('theme', e.target.checked ? 'hide-scroll-buttons' : '');
51+
}
52+
53+
if (e.target.value == 'show-scroll-buttons') {
54+
tabs.setAttribute('theme', e.target.checked ? 'show-scroll-buttons' : '');
55+
}
56+
57+
if (e.target.value == 'icons') {
58+
document.querySelectorAll('vaadin-tab vaadin-icon').forEach((icon) => icon.remove());
59+
if (e.target.checked) {
60+
document.querySelectorAll('vaadin-tab:not(:has(a)), vaadin-tab a').forEach((item) => addIcon(item));
61+
}
62+
}
63+
64+
if (e.target.value == 'anchors') {
65+
if (e.target.checked) {
66+
document.querySelectorAll('vaadin-tab').forEach((tab) => {
67+
const a = document.createElement('a');
68+
a.href = '#';
69+
a.innerHTML = tab.innerHTML;
70+
tab.innerHTML = '';
71+
tab.append(a);
72+
});
73+
} else {
74+
document.querySelectorAll('vaadin-tab a').forEach((a) => {
75+
a.parentElement.innerHTML = a.innerHTML;
76+
});
77+
}
78+
}
79+
});
1480
</script>
81+
82+
<style>
83+
vaadin-tabs[orientation='horizontal'].size {
84+
width: 300px;
85+
}
86+
87+
vaadin-tabs[orientation='vertical'].size {
88+
height: 100px;
89+
}
90+
91+
@media (pointer: coarse) {
92+
vaadin-checkbox[value='hide-scroll-buttons'] {
93+
opacity: 0.3;
94+
}
95+
}
96+
97+
@media (pointer: fine) {
98+
vaadin-checkbox[value='show-scroll-buttons'] {
99+
opacity: 0.3;
100+
}
101+
}
102+
</style>
15103
</head>
16104

17105
<body>
18-
<vaadin-tabs style="max-width: 100%; width: 400px">
19-
<vaadin-tab>
20-
Analytics
21-
<vaadin-tooltip slot="tooltip" text="Analytics tooltip"></vaadin-tooltip>
22-
</vaadin-tab>
23-
<vaadin-tab>
24-
Customers
25-
<vaadin-tooltip slot="tooltip" text="Customers tooltip"></vaadin-tooltip>
26-
</vaadin-tab>
27-
<vaadin-tab>
28-
Dashboards
29-
<vaadin-tooltip slot="tooltip" text="Dashboards tooltip"></vaadin-tooltip>
30-
</vaadin-tab>
31-
<vaadin-tab>
32-
Documents
33-
<vaadin-tooltip slot="tooltip" text="Documents tooltip"></vaadin-tooltip>
34-
</vaadin-tab>
35-
<vaadin-tab>
36-
Orders
37-
<vaadin-tooltip slot="tooltip" text="Orders tooltip"></vaadin-tooltip>
38-
</vaadin-tab>
39-
</vaadin-tabs>
106+
<vaadin-checkbox-group label="Features">
107+
<vaadin-checkbox label="Vertical" value="vertical"></vaadin-checkbox>
108+
<vaadin-checkbox label="Scrolling" value="size"></vaadin-checkbox>
109+
<vaadin-checkbox label="Hide Scroll Buttons" value="hide-scroll-buttons"></vaadin-checkbox>
110+
<vaadin-checkbox label="Show Scroll Buttons" value="show-scroll-buttons"></vaadin-checkbox>
111+
<vaadin-checkbox label="Icons" value="icons"></vaadin-checkbox>
112+
<vaadin-checkbox label="Anchors" value="anchors"></vaadin-checkbox>
113+
</vaadin-checkbox-group>
114+
115+
<section class="section">
116+
<vaadin-tabs>
117+
<vaadin-tab>Analytics</vaadin-tab>
118+
<vaadin-tab>Customers</vaadin-tab>
119+
<vaadin-tab disabled>Dashboards</vaadin-tab>
120+
<vaadin-tab>Documents</vaadin-tab>
121+
<vaadin-tab>Orders</vaadin-tab>
122+
</vaadin-tabs>
123+
</section>
40124
</body>
41125
</html>

packages/tabs/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) 2017 - 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 tabStyles: CSSResult;
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2017 - 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 tabStyles = css`
10+
@layer base {
11+
:host {
12+
flex-shrink: 0;
13+
display: flex;
14+
align-items: center;
15+
justify-content: center;
16+
gap: var(--vaadin-tab-gap, var(--vaadin-gap-container-inline));
17+
padding: var(--vaadin-tab-padding, var(--vaadin-padding-container));
18+
cursor: var(--vaadin-clickable-cursor);
19+
font-size: var(--vaadin-tab-font-size, 1em);
20+
font-weight: var(--vaadin-tab-font-weight, 500);
21+
line-height: var(--vaadin-tab-line-height, inherit);
22+
color: var(--vaadin-tab-color, var(--vaadin-color-subtle));
23+
background: var(--vaadin-tab-background, transparent);
24+
border-radius: var(--vaadin-tab-border-radius, var(--vaadin-radius-m));
25+
-webkit-tap-highlight-color: transparent;
26+
-webkit-user-select: none;
27+
user-select: none;
28+
touch-action: manipulation;
29+
position: relative;
30+
}
31+
32+
:host([hidden]) {
33+
display: none !important;
34+
}
35+
36+
:host([orientation='vertical']) {
37+
justify-content: start;
38+
}
39+
40+
:host([selected]) {
41+
--vaadin-tab-background: var(--vaadin-background-container);
42+
--vaadin-tab-color: var(--vaadin-color);
43+
}
44+
45+
:host([disabled]) {
46+
cursor: var(--vaadin-disabled-cursor);
47+
opacity: 0.5;
48+
}
49+
50+
:host(:is([focus-ring], :focus-visible)) {
51+
outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color);
52+
outline-offset: calc(var(--vaadin-focus-ring-width) * -1);
53+
}
54+
55+
slot {
56+
gap: inherit;
57+
align-items: inherit;
58+
justify-content: inherit;
59+
}
60+
61+
::slotted(a) {
62+
color: inherit;
63+
cursor: inherit;
64+
text-decoration: inherit;
65+
display: flex;
66+
align-items: inherit;
67+
justify-content: inherit;
68+
gap: inherit;
69+
}
70+
71+
::slotted(a)::before {
72+
content: '';
73+
position: absolute;
74+
inset: 0;
75+
}
76+
77+
@media (forced-colors: active) {
78+
:host {
79+
border: 1px solid Canvas;
80+
}
81+
82+
:host([selected]) {
83+
color: Highlight;
84+
border-color: Highlight;
85+
}
86+
87+
:host([disabled]) {
88+
color: GrayText;
89+
opacity: 1;
90+
}
91+
}
92+
}
93+
`;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2017 - 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 tabsStyles: CSSResult;
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2017 - 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 tabsStyles = css`
10+
@layer base {
11+
:host {
12+
display: flex;
13+
max-width: 100%;
14+
max-height: 100%;
15+
position: relative;
16+
}
17+
18+
:host([hidden]) {
19+
display: none !important;
20+
}
21+
22+
:host([orientation='vertical']) {
23+
flex-direction: column;
24+
}
25+
26+
[part='tabs'] {
27+
flex: 1;
28+
overflow: auto;
29+
overscroll-behavior: contain;
30+
display: flex;
31+
flex-direction: column;
32+
gap: var(--vaadin-tabs-gap, var(--vaadin-gap-container-inline));
33+
}
34+
35+
:host([orientation='horizontal']) [part='tabs'] {
36+
flex-direction: row;
37+
scrollbar-width: none;
38+
}
39+
40+
/* scrollbar-width is supported in Safari 18.2, use the following for earlier */
41+
:host([orientation='horizontal']) [part='tabs']::-webkit-scrollbar {
42+
display: none;
43+
}
44+
45+
[part$='button'] {
46+
position: absolute;
47+
z-index: 1;
48+
pointer-events: none;
49+
opacity: 0;
50+
cursor: var(--vaadin-clickable-cursor);
51+
box-sizing: border-box;
52+
height: 100%;
53+
padding: var(--vaadin-tab-padding, var(--vaadin-padding-container));
54+
background: var(--vaadin-background-color);
55+
display: flex;
56+
align-items: center;
57+
justify-content: center;
58+
-webkit-tap-highlight-color: transparent;
59+
touch-action: manipulation;
60+
}
61+
62+
[part='forward-button'] {
63+
inset-inline-end: 0;
64+
}
65+
66+
:host([overflow~='start']) [part='back-button'],
67+
:host([overflow~='end']) [part='forward-button'] {
68+
pointer-events: auto;
69+
opacity: 1;
70+
}
71+
72+
[part$='button']::before {
73+
content: '';
74+
display: block;
75+
width: var(--vaadin-icon-size, 1lh);
76+
height: var(--vaadin-icon-size, 1lh);
77+
background: currentColor;
78+
mask-image: var(--_vaadin-icon-chevron-down);
79+
rotate: 90deg;
80+
}
81+
82+
[part='forward-button']::before {
83+
rotate: -90deg;
84+
}
85+
86+
:host(:is([orientation='vertical'], [theme~='hide-scroll-buttons'])) [part$='button'] {
87+
display: none;
88+
}
89+
90+
@media (pointer: coarse) {
91+
:host(:not([theme~='show-scroll-buttons'])) [part$='button'] {
92+
display: none;
93+
}
94+
}
95+
96+
:host([dir='rtl']) [part$='button']::before {
97+
scale: 1 -1;
98+
}
99+
100+
@media (forced-colors: active) {
101+
[part$='button']::before {
102+
background: CanvasText;
103+
}
104+
}
105+
}
106+
`;

packages/tabs/test/overflow.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe('overflow', () => {
4848
if (orientation === 'horizontal') {
4949
tabs.style.width = '200px';
5050
} else {
51-
tabs.style.height = '100px';
51+
tabs.style.height = '120px';
5252
}
5353
await nextResize(tabs);
5454
await nextRender();

0 commit comments

Comments
 (0)