@teseor/css 1.3.0 → 1.4.0
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/dist/compiled.css +1 -1
- package/dist/index.css +14 -9
- package/package.json +1 -1
- package/src/03-layout/app-shell/app-shell.api.json +18 -0
- package/src/03-layout/app-shell/app-shell.docs.json +18 -0
- package/src/03-layout/content/content-visual.png +0 -0
- package/src/03-layout/content/content.api.json +26 -0
- package/src/03-layout/content/content.docs.json +82 -0
- package/src/03-layout/content/content.visual.spec.ts +14 -0
- package/src/03-layout/content/index.scss +25 -0
- package/src/03-layout/footer/footer-visual.png +0 -0
- package/src/03-layout/footer/footer.api.json +45 -0
- package/src/03-layout/footer/footer.docs.json +110 -0
- package/src/03-layout/footer/footer.visual.spec.ts +14 -0
- package/src/03-layout/footer/index.scss +62 -0
- package/src/03-layout/index.scss +11 -6
- package/src/03-layout/main/index.scss +1 -0
- package/src/03-layout/nav-rail/index.scss +50 -0
- package/src/03-layout/nav-rail/nav-rail-visual.png +0 -0
- package/src/03-layout/nav-rail/nav-rail.api.json +35 -0
- package/src/03-layout/nav-rail/nav-rail.docs.json +76 -0
- package/src/03-layout/nav-rail/nav-rail.visual.spec.ts +14 -0
- package/src/03-layout/page-header/index.scss +52 -0
- package/src/03-layout/page-header/page-header-visual.png +0 -0
- package/src/03-layout/page-header/page-header.api.json +35 -0
- package/src/03-layout/page-header/page-header.docs.json +124 -0
- package/src/03-layout/page-header/page-header.visual.spec.ts +14 -0
- package/src/03-layout/sidebar/index.scss +2 -2
- package/src/03-layout/topbar/index.scss +62 -0
- package/src/03-layout/topbar/topbar-visual.png +0 -0
- package/src/03-layout/topbar/topbar.api.json +45 -0
- package/src/03-layout/topbar/topbar.docs.json +110 -0
- package/src/03-layout/topbar/topbar.visual.spec.ts +14 -0
- package/src/04-components/data-display/avatar/avatar-visual.png +0 -0
- package/src/04-components/disclosure/accordion/accordion-visual.png +0 -0
- package/src/04-components/disclosure/disclosure/disclosure-visual.png +0 -0
- package/src/04-components/forms/field/field-visual.png +0 -0
- package/src/04-components/forms/input/input-visual.png +0 -0
- package/src/04-components/index.scss +1 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
@layer primitives {
|
|
2
|
+
.nav-rail {
|
|
3
|
+
--_width: var(--ui-nav-rail-width, var(--ui-row-4));
|
|
4
|
+
--_bg: var(--ui-nav-rail-bg, var(--ui-color-bg-subtle));
|
|
5
|
+
--_border-color: var(--ui-nav-rail-border-color, var(--ui-color-border));
|
|
6
|
+
--_gap: var(--ui-nav-rail-gap, var(--ui-space-1));
|
|
7
|
+
--_z: var(--ui-nav-rail-z, var(--ui-z-sticky));
|
|
8
|
+
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
align-items: center;
|
|
12
|
+
position: fixed;
|
|
13
|
+
inset-block: var(--topbar-height, 0);
|
|
14
|
+
inset-inline-start: 0;
|
|
15
|
+
z-index: var(--_z);
|
|
16
|
+
|
|
17
|
+
inline-size: var(--_width);
|
|
18
|
+
padding-block: var(--ui-space-2);
|
|
19
|
+
|
|
20
|
+
background: var(--_bg);
|
|
21
|
+
box-shadow: inset calc(var(--ui-border-width-sm) * -1) 0 0 0 var(--_border-color);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Navigation items area
|
|
25
|
+
.nav-rail__items {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex: 1;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
align-items: center;
|
|
30
|
+
gap: var(--_gap);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Bottom-pinned actions (settings, profile)
|
|
34
|
+
.nav-rail__actions {
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: var(--_gap);
|
|
39
|
+
|
|
40
|
+
margin-block-start: auto;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Position on end side
|
|
44
|
+
.nav-rail--end {
|
|
45
|
+
inset-inline-start: auto;
|
|
46
|
+
inset-inline-end: 0;
|
|
47
|
+
|
|
48
|
+
box-shadow: inset var(--ui-border-width-sm) 0 0 0 var(--_border-color);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nav-rail",
|
|
3
|
+
"element": "nav",
|
|
4
|
+
"modifiers": {
|
|
5
|
+
"end": {
|
|
6
|
+
"type": "boolean"
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
"elements": {
|
|
10
|
+
"items": {},
|
|
11
|
+
"actions": {}
|
|
12
|
+
},
|
|
13
|
+
"cssVars": [
|
|
14
|
+
{
|
|
15
|
+
"name": "--ui-nav-rail-width",
|
|
16
|
+
"default": "var(--ui-row-4)"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"name": "--ui-nav-rail-bg",
|
|
20
|
+
"default": "var(--ui-color-bg-subtle)"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "--ui-nav-rail-border-color",
|
|
24
|
+
"default": "var(--ui-color-border)"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "--ui-nav-rail-gap",
|
|
28
|
+
"default": "var(--ui-space-1)"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "--ui-nav-rail-z",
|
|
32
|
+
"default": "var(--ui-z-sticky)"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "nav-rail",
|
|
3
|
+
"type": "primitive",
|
|
4
|
+
"title": "Nav Rail",
|
|
5
|
+
"description": "Narrow fixed vertical navigation rail for icon-based navigation with bottom actions.",
|
|
6
|
+
"api": "./nav-rail.api.json",
|
|
7
|
+
"sections": [
|
|
8
|
+
{
|
|
9
|
+
"title": "Default",
|
|
10
|
+
"description": "Fixed narrow rail with icon items and bottom actions.",
|
|
11
|
+
"examples": [
|
|
12
|
+
{
|
|
13
|
+
"items": [
|
|
14
|
+
{
|
|
15
|
+
"tag": "nav",
|
|
16
|
+
"class": "ui-nav-rail",
|
|
17
|
+
"style": "position: relative; block-size: 320px",
|
|
18
|
+
"children": [
|
|
19
|
+
{
|
|
20
|
+
"tag": "div",
|
|
21
|
+
"class": "ui-nav-rail__items",
|
|
22
|
+
"children": [
|
|
23
|
+
{
|
|
24
|
+
"tag": "button",
|
|
25
|
+
"class": "ui-button ui-button--ghost ui-button--icon",
|
|
26
|
+
"text": "H"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"tag": "button",
|
|
30
|
+
"class": "ui-button ui-button--ghost ui-button--icon",
|
|
31
|
+
"text": "S"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"tag": "button",
|
|
35
|
+
"class": "ui-button ui-button--ghost ui-button--icon",
|
|
36
|
+
"text": "M"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"tag": "div",
|
|
42
|
+
"class": "ui-nav-rail__actions",
|
|
43
|
+
"children": [
|
|
44
|
+
{
|
|
45
|
+
"tag": "button",
|
|
46
|
+
"class": "ui-button ui-button--ghost ui-button--icon",
|
|
47
|
+
"text": "G"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"title": "End Position",
|
|
59
|
+
"description": "Rail on the end (right in LTR) side.",
|
|
60
|
+
"examples": [
|
|
61
|
+
{
|
|
62
|
+
"code": "<nav class=\"ui-nav-rail ui-nav-rail--end\">\n <div class=\"ui-nav-rail__items\">\n <!-- icon buttons -->\n </div>\n <div class=\"ui-nav-rail__actions\">\n <!-- settings, profile -->\n </div>\n</nav>"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"title": "With Topbar",
|
|
68
|
+
"description": "Nav rail respects --topbar-height, offsetting below the topbar.",
|
|
69
|
+
"examples": [
|
|
70
|
+
{
|
|
71
|
+
"code": "<body class=\"ui-app-shell\" style=\"--topbar-height: var(--ui-row-3)\">\n <header class=\"ui-topbar ui-topbar--fixed ui-topbar--bordered\">\n <div class=\"ui-topbar__start\"><strong>App</strong></div>\n </header>\n <nav class=\"ui-nav-rail\">\n <div class=\"ui-nav-rail__items\"><!-- icons --></div>\n </nav>\n</body>"
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { expect, test } from '@playwright/test';
|
|
3
|
+
import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../testing';
|
|
4
|
+
|
|
5
|
+
const DOCS_PATH = resolve(__dirname, 'nav-rail.docs.json');
|
|
6
|
+
|
|
7
|
+
test.describe('nav-rail visual regression', () => {
|
|
8
|
+
test('all variations', async ({ page }) => {
|
|
9
|
+
await setupVisualTestFromDocs(page, DOCS_PATH);
|
|
10
|
+
await validateGridRhythm(page, 'nav-rail');
|
|
11
|
+
await saveForLostPixel(page, 'nav-rail');
|
|
12
|
+
await expect(page.locator('body')).toHaveScreenshot('nav-rail.visual.png');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
@layer primitives {
|
|
2
|
+
.page-header {
|
|
3
|
+
--_padding-block: var(--ui-page-header-padding-block, var(--ui-space-3));
|
|
4
|
+
--_padding-inline: var(--ui-page-header-padding-inline, 0);
|
|
5
|
+
--_gap: var(--ui-page-header-gap, var(--ui-space-2));
|
|
6
|
+
--_border-color: var(--ui-page-header-border-color, var(--ui-color-border));
|
|
7
|
+
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-wrap: wrap;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: var(--_gap);
|
|
12
|
+
|
|
13
|
+
padding-block: var(--_padding-block);
|
|
14
|
+
padding-inline: var(--_padding-inline);
|
|
15
|
+
|
|
16
|
+
line-height: var(--ui-body-line-height);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Title area: stacks title + description vertically
|
|
20
|
+
.page-header__title {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex: 1;
|
|
23
|
+
flex-direction: column;
|
|
24
|
+
gap: var(--ui-space-1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Actions slot: right-aligned buttons/controls
|
|
28
|
+
.page-header__actions {
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-shrink: 0;
|
|
31
|
+
align-items: center;
|
|
32
|
+
gap: var(--ui-space-2);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Breadcrumb slot: full-width above title row
|
|
36
|
+
.page-header__breadcrumb {
|
|
37
|
+
flex-basis: 100%;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Modifiers
|
|
41
|
+
.page-header--bordered {
|
|
42
|
+
box-shadow: inset 0 calc(var(--ui-border-width-sm) * -1) 0 0 var(--_border-color);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.page-header--sticky {
|
|
46
|
+
position: sticky;
|
|
47
|
+
inset-block-start: 0;
|
|
48
|
+
z-index: var(--ui-z-sticky);
|
|
49
|
+
|
|
50
|
+
background: var(--ui-color-bg);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "page-header",
|
|
3
|
+
"element": "header",
|
|
4
|
+
"modifiers": {
|
|
5
|
+
"bordered": {
|
|
6
|
+
"type": "boolean"
|
|
7
|
+
},
|
|
8
|
+
"sticky": {
|
|
9
|
+
"type": "boolean"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"elements": {
|
|
13
|
+
"title": {},
|
|
14
|
+
"actions": {},
|
|
15
|
+
"breadcrumb": {}
|
|
16
|
+
},
|
|
17
|
+
"cssVars": [
|
|
18
|
+
{
|
|
19
|
+
"name": "--ui-page-header-padding-block",
|
|
20
|
+
"default": "var(--ui-space-3)"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "--ui-page-header-padding-inline",
|
|
24
|
+
"default": "0"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "--ui-page-header-gap",
|
|
28
|
+
"default": "var(--ui-space-2)"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "--ui-page-header-border-color",
|
|
32
|
+
"default": "var(--ui-color-border)"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "page-header",
|
|
3
|
+
"type": "primitive",
|
|
4
|
+
"title": "Page Header",
|
|
5
|
+
"description": "Content area header with title, description, actions, and optional breadcrumb.",
|
|
6
|
+
"api": "./page-header.api.json",
|
|
7
|
+
"sections": [
|
|
8
|
+
{
|
|
9
|
+
"title": "Default",
|
|
10
|
+
"description": "Title with actions on the end side.",
|
|
11
|
+
"examples": [
|
|
12
|
+
{
|
|
13
|
+
"items": [
|
|
14
|
+
{
|
|
15
|
+
"tag": "header",
|
|
16
|
+
"class": "ui-page-header ui-page-header--bordered",
|
|
17
|
+
"children": [
|
|
18
|
+
{
|
|
19
|
+
"tag": "div",
|
|
20
|
+
"class": "ui-page-header__title",
|
|
21
|
+
"children": [
|
|
22
|
+
{ "tag": "h1", "class": "ui-heading ui-heading--xl", "text": "Users" },
|
|
23
|
+
{ "tag": "p", "text": "Manage user accounts and permissions." }
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"tag": "div",
|
|
28
|
+
"class": "ui-page-header__actions",
|
|
29
|
+
"children": [
|
|
30
|
+
{ "tag": "button", "class": "ui-button ui-button--primary", "text": "Add user" }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"title": "With Breadcrumb",
|
|
41
|
+
"description": "Breadcrumb spans the full width above the title row.",
|
|
42
|
+
"examples": [
|
|
43
|
+
{
|
|
44
|
+
"items": [
|
|
45
|
+
{
|
|
46
|
+
"tag": "header",
|
|
47
|
+
"class": "ui-page-header ui-page-header--bordered",
|
|
48
|
+
"children": [
|
|
49
|
+
{
|
|
50
|
+
"tag": "nav",
|
|
51
|
+
"class": "ui-page-header__breadcrumb",
|
|
52
|
+
"children": [{ "tag": "span", "text": "Home / Settings / Users" }]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"tag": "div",
|
|
56
|
+
"class": "ui-page-header__title",
|
|
57
|
+
"children": [
|
|
58
|
+
{ "tag": "h1", "class": "ui-heading ui-heading--xl", "text": "Users" }
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"tag": "div",
|
|
63
|
+
"class": "ui-page-header__actions",
|
|
64
|
+
"children": [{ "tag": "button", "class": "ui-button", "text": "Export" }]
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"title": "Bordered",
|
|
74
|
+
"description": "Bottom border via inset box-shadow.",
|
|
75
|
+
"examples": [
|
|
76
|
+
{
|
|
77
|
+
"items": [
|
|
78
|
+
{
|
|
79
|
+
"tag": "header",
|
|
80
|
+
"class": "ui-page-header ui-page-header--bordered",
|
|
81
|
+
"children": [
|
|
82
|
+
{
|
|
83
|
+
"tag": "div",
|
|
84
|
+
"class": "ui-page-header__title",
|
|
85
|
+
"children": [
|
|
86
|
+
{ "tag": "h1", "class": "ui-heading ui-heading--xl", "text": "Dashboard" }
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"title": "Sticky",
|
|
97
|
+
"description": "Sticks to the top of the scroll container.",
|
|
98
|
+
"examples": [
|
|
99
|
+
{
|
|
100
|
+
"items": [
|
|
101
|
+
{
|
|
102
|
+
"tag": "header",
|
|
103
|
+
"class": "ui-page-header ui-page-header--sticky ui-page-header--bordered",
|
|
104
|
+
"children": [
|
|
105
|
+
{
|
|
106
|
+
"tag": "div",
|
|
107
|
+
"class": "ui-page-header__title",
|
|
108
|
+
"children": [
|
|
109
|
+
{ "tag": "h1", "class": "ui-heading ui-heading--xl", "text": "Sticky Header" }
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"tag": "div",
|
|
114
|
+
"class": "ui-page-header__actions",
|
|
115
|
+
"children": [{ "tag": "button", "class": "ui-button", "text": "Save" }]
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { expect, test } from '@playwright/test';
|
|
3
|
+
import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../testing';
|
|
4
|
+
|
|
5
|
+
const DOCS_PATH = resolve(__dirname, 'page-header.docs.json');
|
|
6
|
+
|
|
7
|
+
test.describe('page-header visual regression', () => {
|
|
8
|
+
test('all variations', async ({ page }) => {
|
|
9
|
+
await setupVisualTestFromDocs(page, DOCS_PATH);
|
|
10
|
+
await validateGridRhythm(page, 'page-header');
|
|
11
|
+
await saveForLostPixel(page, 'page-header');
|
|
12
|
+
await expect(page.locator('body')).toHaveScreenshot('page-header.visual.png');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
display: flex;
|
|
4
4
|
flex-direction: column;
|
|
5
5
|
position: fixed;
|
|
6
|
-
inset-block-start: 0;
|
|
6
|
+
inset-block-start: var(--topbar-height, 0);
|
|
7
7
|
inset-inline-start: 0;
|
|
8
8
|
z-index: var(--ui-z-sticky);
|
|
9
9
|
|
|
10
|
-
block-size: 100vh;
|
|
10
|
+
block-size: calc(100vh - var(--topbar-height, 0px));
|
|
11
11
|
inline-size: var(--sidebar-width, calc(var(--unit) * 30));
|
|
12
12
|
overflow-y: auto;
|
|
13
13
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
@layer primitives {
|
|
2
|
+
.topbar {
|
|
3
|
+
--_height: var(--ui-topbar-height, var(--ui-row-3));
|
|
4
|
+
--_bg: var(--ui-topbar-bg, var(--ui-color-bg));
|
|
5
|
+
--_padding-inline: var(--ui-topbar-padding-inline, var(--ui-space-3));
|
|
6
|
+
--_border-color: var(--ui-topbar-border-color, var(--ui-color-border));
|
|
7
|
+
--_z: var(--ui-topbar-z, var(--ui-z-sticky));
|
|
8
|
+
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
z-index: var(--_z);
|
|
12
|
+
|
|
13
|
+
block-size: var(--_height);
|
|
14
|
+
padding-inline: var(--_padding-inline);
|
|
15
|
+
|
|
16
|
+
background: var(--_bg);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Sections
|
|
20
|
+
.topbar__start {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-shrink: 0;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: var(--ui-space-2);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.topbar__center {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex: 1;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
gap: var(--ui-space-2);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.topbar__end {
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-shrink: 0;
|
|
38
|
+
align-items: center;
|
|
39
|
+
gap: var(--ui-space-2);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Position modifiers
|
|
43
|
+
.topbar--sticky {
|
|
44
|
+
position: sticky;
|
|
45
|
+
inset-block-start: 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.topbar--fixed {
|
|
49
|
+
position: fixed;
|
|
50
|
+
inset-block-start: 0;
|
|
51
|
+
inset-inline: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Visual modifiers
|
|
55
|
+
.topbar--bordered {
|
|
56
|
+
box-shadow: inset 0 calc(var(--ui-border-width-sm) * -1) 0 0 var(--_border-color);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.topbar--raised {
|
|
60
|
+
box-shadow: var(--shadow-sm);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "topbar",
|
|
3
|
+
"element": "header",
|
|
4
|
+
"modifiers": {
|
|
5
|
+
"sticky": {
|
|
6
|
+
"type": "boolean"
|
|
7
|
+
},
|
|
8
|
+
"fixed": {
|
|
9
|
+
"type": "boolean"
|
|
10
|
+
},
|
|
11
|
+
"bordered": {
|
|
12
|
+
"type": "boolean"
|
|
13
|
+
},
|
|
14
|
+
"raised": {
|
|
15
|
+
"type": "boolean"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"elements": {
|
|
19
|
+
"start": {},
|
|
20
|
+
"center": {},
|
|
21
|
+
"end": {}
|
|
22
|
+
},
|
|
23
|
+
"cssVars": [
|
|
24
|
+
{
|
|
25
|
+
"name": "--ui-topbar-height",
|
|
26
|
+
"default": "var(--ui-row-3)"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "--ui-topbar-bg",
|
|
30
|
+
"default": "var(--ui-color-bg)"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "--ui-topbar-padding-inline",
|
|
34
|
+
"default": "var(--ui-space-3)"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "--ui-topbar-border-color",
|
|
38
|
+
"default": "var(--ui-color-border)"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "--ui-topbar-z",
|
|
42
|
+
"default": "var(--ui-z-sticky)"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "topbar",
|
|
3
|
+
"type": "primitive",
|
|
4
|
+
"title": "Topbar",
|
|
5
|
+
"description": "Fixed or sticky header bar for app layouts with start, center, and end sections.",
|
|
6
|
+
"api": "./topbar.api.json",
|
|
7
|
+
"sections": [
|
|
8
|
+
{
|
|
9
|
+
"title": "Default",
|
|
10
|
+
"description": "Basic topbar with three sections: start (brand), center (search/nav), and end (actions).",
|
|
11
|
+
"examples": [
|
|
12
|
+
{
|
|
13
|
+
"items": [
|
|
14
|
+
{
|
|
15
|
+
"tag": "header",
|
|
16
|
+
"class": "ui-topbar ui-topbar--bordered",
|
|
17
|
+
"children": [
|
|
18
|
+
{
|
|
19
|
+
"tag": "div",
|
|
20
|
+
"class": "ui-topbar__start",
|
|
21
|
+
"children": [{ "tag": "strong", "text": "Brand" }]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"tag": "div",
|
|
25
|
+
"class": "ui-topbar__center",
|
|
26
|
+
"children": [{ "tag": "span", "text": "Center content" }]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"tag": "div",
|
|
30
|
+
"class": "ui-topbar__end",
|
|
31
|
+
"children": [{ "tag": "span", "text": "Actions" }]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"title": "Sticky",
|
|
41
|
+
"description": "Stays at the top of the viewport on scroll.",
|
|
42
|
+
"examples": [
|
|
43
|
+
{
|
|
44
|
+
"code": "<header class=\"ui-topbar ui-topbar--sticky ui-topbar--bordered\">\n <div class=\"ui-topbar__start\"><strong>Brand</strong></div>\n <div class=\"ui-topbar__center\">Nav</div>\n <div class=\"ui-topbar__end\">Actions</div>\n</header>"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"title": "Bordered",
|
|
50
|
+
"description": "Bottom border via inset box-shadow to preserve grid rhythm.",
|
|
51
|
+
"examples": [
|
|
52
|
+
{
|
|
53
|
+
"items": [
|
|
54
|
+
{
|
|
55
|
+
"tag": "header",
|
|
56
|
+
"class": "ui-topbar ui-topbar--bordered",
|
|
57
|
+
"children": [
|
|
58
|
+
{
|
|
59
|
+
"tag": "div",
|
|
60
|
+
"class": "ui-topbar__start",
|
|
61
|
+
"children": [{ "tag": "strong", "text": "Bordered" }]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"tag": "div",
|
|
65
|
+
"class": "ui-topbar__end",
|
|
66
|
+
"children": [{ "tag": "span", "text": "Actions" }]
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"title": "Raised",
|
|
76
|
+
"description": "Subtle drop shadow below the topbar.",
|
|
77
|
+
"examples": [
|
|
78
|
+
{
|
|
79
|
+
"items": [
|
|
80
|
+
{
|
|
81
|
+
"tag": "header",
|
|
82
|
+
"class": "ui-topbar ui-topbar--raised",
|
|
83
|
+
"children": [
|
|
84
|
+
{
|
|
85
|
+
"tag": "div",
|
|
86
|
+
"class": "ui-topbar__start",
|
|
87
|
+
"children": [{ "tag": "strong", "text": "Raised" }]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"tag": "div",
|
|
91
|
+
"class": "ui-topbar__end",
|
|
92
|
+
"children": [{ "tag": "span", "text": "Actions" }]
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"title": "Integration with Sidebar",
|
|
102
|
+
"description": "Set --topbar-height on .app-shell so sidebar and main offset correctly. The topbar itself uses --fixed, and sidebar/main read --topbar-height.",
|
|
103
|
+
"examples": [
|
|
104
|
+
{
|
|
105
|
+
"code": "<body class=\"ui-app-shell\" style=\"--topbar-height: var(--ui-row-3)\">\n <header class=\"ui-topbar ui-topbar--fixed ui-topbar--bordered\">\n <div class=\"ui-topbar__start\"><strong>App</strong></div>\n <div class=\"ui-topbar__end\">User</div>\n </header>\n <aside class=\"ui-sidebar\">Sidebar</aside>\n <main class=\"ui-main\">\n <div class=\"ui-container\">Content</div>\n </main>\n</body>"
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { expect, test } from '@playwright/test';
|
|
3
|
+
import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../testing';
|
|
4
|
+
|
|
5
|
+
const DOCS_PATH = resolve(__dirname, 'topbar.docs.json');
|
|
6
|
+
|
|
7
|
+
test.describe('topbar visual regression', () => {
|
|
8
|
+
test('all variations', async ({ page }) => {
|
|
9
|
+
await setupVisualTestFromDocs(page, DOCS_PATH);
|
|
10
|
+
await validateGridRhythm(page, 'topbar');
|
|
11
|
+
await saveForLostPixel(page, 'topbar');
|
|
12
|
+
await expect(page.locator('body')).toHaveScreenshot('topbar.visual.png');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|