@teseor/css 1.2.4 → 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 +23 -7
- 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/aspect-ratio/aspect-ratio-visual.png +0 -0
- package/src/03-layout/aspect-ratio/aspect-ratio.api.json +16 -0
- package/src/03-layout/aspect-ratio/aspect-ratio.docs.json +193 -0
- package/src/03-layout/aspect-ratio/aspect-ratio.visual.spec.ts +14 -0
- package/src/03-layout/aspect-ratio/index.scss +23 -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 -5
- 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/actions/button-group/button-group-visual.png +0 -0
- package/src/04-components/actions/button-group/button-group.visual.spec.ts +1 -2
- package/src/04-components/actions/button-group/index.scss +3 -1
- package/src/04-components/data-display/avatar/avatar-visual.png +0 -0
- package/src/04-components/data-display/stat/index.scss +47 -0
- package/src/04-components/data-display/stat/stat-visual.png +0 -0
- package/src/04-components/data-display/stat/stat.api.json +18 -0
- package/src/04-components/data-display/stat/stat.docs.json +114 -0
- package/src/04-components/data-display/stat/stat.visual.spec.ts +14 -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 +5 -0
- package/src/04-components/layout/spacer/index.scss +11 -0
- package/src/04-components/layout/spacer/spacer-visual.png +0 -0
- package/src/04-components/layout/spacer/spacer.api.json +6 -0
- package/src/04-components/layout/spacer/spacer.docs.json +102 -0
- package/src/04-components/layout/spacer/spacer.visual.spec.ts +14 -0
- package/src/04-components/typography/blockquote/blockquote-visual.png +0 -0
- package/src/04-components/typography/blockquote/blockquote.api.json +18 -0
- package/src/04-components/typography/blockquote/blockquote.docs.json +89 -0
- package/src/04-components/typography/blockquote/blockquote.visual.spec.ts +14 -0
- package/src/04-components/typography/blockquote/index.scss +41 -0
- package/src/04-components/typography/kbd/index.scss +33 -0
- package/src/04-components/typography/kbd/kbd-visual.png +0 -0
- package/src/04-components/typography/kbd/kbd.api.json +15 -0
- package/src/04-components/typography/kbd/kbd.docs.json +61 -0
- package/src/04-components/typography/kbd/kbd.visual.spec.ts +14 -0
- package/src/04-components/typography/mark/index.scss +22 -0
- package/src/04-components/typography/mark/mark-visual.png +0 -0
- package/src/04-components/typography/mark/mark.api.json +12 -0
- package/src/04-components/typography/mark/mark.docs.json +60 -0
- package/src/04-components/typography/mark/mark.visual.spec.ts +14 -0
|
@@ -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, 'content.docs.json');
|
|
6
|
+
|
|
7
|
+
test.describe('content visual regression', () => {
|
|
8
|
+
test('all variations', async ({ page }) => {
|
|
9
|
+
await setupVisualTestFromDocs(page, DOCS_PATH);
|
|
10
|
+
await validateGridRhythm(page, 'content');
|
|
11
|
+
await saveForLostPixel(page, 'content');
|
|
12
|
+
await expect(page.locator('body')).toHaveScreenshot('content.visual.png');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
@layer primitives {
|
|
2
|
+
.content {
|
|
3
|
+
--_padding-block: var(--ui-content-padding-block, var(--ui-space-4));
|
|
4
|
+
--_padding-inline: var(--ui-content-padding-inline, var(--ui-space-4));
|
|
5
|
+
--_gap: var(--ui-content-gap, var(--ui-space-3));
|
|
6
|
+
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
gap: var(--_gap);
|
|
10
|
+
|
|
11
|
+
padding-block: var(--_padding-block);
|
|
12
|
+
padding-inline: var(--_padding-inline);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Prose-friendly: narrower max-width for readability
|
|
16
|
+
.content--prose {
|
|
17
|
+
max-inline-size: calc(var(--unit) * 80);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Flush: no padding, just gap
|
|
21
|
+
.content--flush {
|
|
22
|
+
padding-block: 0;
|
|
23
|
+
padding-inline: 0;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "footer",
|
|
3
|
+
"element": "footer",
|
|
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-footer-height",
|
|
26
|
+
"default": "var(--ui-row-3)"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "--ui-footer-bg",
|
|
30
|
+
"default": "var(--ui-color-bg)"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "--ui-footer-padding-inline",
|
|
34
|
+
"default": "var(--ui-space-3)"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "--ui-footer-border-color",
|
|
38
|
+
"default": "var(--ui-color-border)"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "--ui-footer-z",
|
|
42
|
+
"default": "var(--ui-z-sticky)"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "footer",
|
|
3
|
+
"type": "primitive",
|
|
4
|
+
"title": "Footer",
|
|
5
|
+
"description": "Page footer with start, center, and end sections. Supports sticky/fixed positioning and border/shadow variants.",
|
|
6
|
+
"api": "./footer.api.json",
|
|
7
|
+
"sections": [
|
|
8
|
+
{
|
|
9
|
+
"title": "Default",
|
|
10
|
+
"description": "Basic footer with three sections.",
|
|
11
|
+
"examples": [
|
|
12
|
+
{
|
|
13
|
+
"items": [
|
|
14
|
+
{
|
|
15
|
+
"tag": "footer",
|
|
16
|
+
"class": "ui-footer ui-footer--bordered",
|
|
17
|
+
"children": [
|
|
18
|
+
{
|
|
19
|
+
"tag": "div",
|
|
20
|
+
"class": "ui-footer__start",
|
|
21
|
+
"children": [{ "tag": "small", "text": "2026 Brand" }]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"tag": "div",
|
|
25
|
+
"class": "ui-footer__center",
|
|
26
|
+
"children": [{ "tag": "span", "text": "Footer links" }]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"tag": "div",
|
|
30
|
+
"class": "ui-footer__end",
|
|
31
|
+
"children": [{ "tag": "small", "text": "v1.0.0" }]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"title": "Sticky",
|
|
41
|
+
"description": "Stays at the bottom of the viewport on scroll.",
|
|
42
|
+
"examples": [
|
|
43
|
+
{
|
|
44
|
+
"code": "<footer class=\"ui-footer ui-footer--sticky ui-footer--bordered\">\n <div class=\"ui-footer__start\"><small>Brand</small></div>\n <div class=\"ui-footer__center\">Links</div>\n <div class=\"ui-footer__end\"><small>v1.0.0</small></div>\n</footer>"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"title": "Bordered",
|
|
50
|
+
"description": "Top border via inset box-shadow to preserve grid rhythm.",
|
|
51
|
+
"examples": [
|
|
52
|
+
{
|
|
53
|
+
"items": [
|
|
54
|
+
{
|
|
55
|
+
"tag": "footer",
|
|
56
|
+
"class": "ui-footer ui-footer--bordered",
|
|
57
|
+
"children": [
|
|
58
|
+
{
|
|
59
|
+
"tag": "div",
|
|
60
|
+
"class": "ui-footer__start",
|
|
61
|
+
"children": [{ "tag": "small", "text": "Bordered footer" }]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"tag": "div",
|
|
65
|
+
"class": "ui-footer__end",
|
|
66
|
+
"children": [{ "tag": "small", "text": "v1.0.0" }]
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"title": "Raised",
|
|
76
|
+
"description": "Subtle drop shadow above the footer.",
|
|
77
|
+
"examples": [
|
|
78
|
+
{
|
|
79
|
+
"items": [
|
|
80
|
+
{
|
|
81
|
+
"tag": "footer",
|
|
82
|
+
"class": "ui-footer ui-footer--raised",
|
|
83
|
+
"children": [
|
|
84
|
+
{
|
|
85
|
+
"tag": "div",
|
|
86
|
+
"class": "ui-footer__start",
|
|
87
|
+
"children": [{ "tag": "small", "text": "Raised footer" }]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"tag": "div",
|
|
91
|
+
"class": "ui-footer__end",
|
|
92
|
+
"children": [{ "tag": "small", "text": "v1.0.0" }]
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"title": "Full Layout",
|
|
102
|
+
"description": "Footer inside an app-shell with topbar and sidebar.",
|
|
103
|
+
"examples": [
|
|
104
|
+
{
|
|
105
|
+
"code": "<body class=\"ui-app-shell\">\n <header class=\"ui-topbar ui-topbar--bordered\">\n <div class=\"ui-topbar__start\"><strong>App</strong></div>\n </header>\n <main class=\"ui-main ui-main--full\">\n <div class=\"ui-container\">Content</div>\n <footer class=\"ui-footer ui-footer--bordered\">\n <div class=\"ui-footer__start\"><small>Brand</small></div>\n <div class=\"ui-footer__end\"><small>v1.0.0</small></div>\n </footer>\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, 'footer.docs.json');
|
|
6
|
+
|
|
7
|
+
test.describe('footer visual regression', () => {
|
|
8
|
+
test('all variations', async ({ page }) => {
|
|
9
|
+
await setupVisualTestFromDocs(page, DOCS_PATH);
|
|
10
|
+
await validateGridRhythm(page, 'footer');
|
|
11
|
+
await saveForLostPixel(page, 'footer');
|
|
12
|
+
await expect(page.locator('body')).toHaveScreenshot('footer.visual.png');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
@layer primitives {
|
|
2
|
+
.footer {
|
|
3
|
+
--_height: var(--ui-footer-height, var(--ui-row-3));
|
|
4
|
+
--_bg: var(--ui-footer-bg, var(--ui-color-bg));
|
|
5
|
+
--_padding-inline: var(--ui-footer-padding-inline, var(--ui-space-3));
|
|
6
|
+
--_border-color: var(--ui-footer-border-color, var(--ui-color-border));
|
|
7
|
+
--_z: var(--ui-footer-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
|
+
.footer__start {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-shrink: 0;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: var(--ui-space-2);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.footer__center {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex: 1;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
gap: var(--ui-space-2);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.footer__end {
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-shrink: 0;
|
|
38
|
+
align-items: center;
|
|
39
|
+
gap: var(--ui-space-2);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Position modifiers
|
|
43
|
+
.footer--sticky {
|
|
44
|
+
position: sticky;
|
|
45
|
+
inset-block-end: 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.footer--fixed {
|
|
49
|
+
position: fixed;
|
|
50
|
+
inset-block-end: 0;
|
|
51
|
+
inset-inline: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Visual modifiers
|
|
55
|
+
.footer--bordered {
|
|
56
|
+
box-shadow: inset 0 var(--ui-border-width-sm) 0 0 var(--_border-color);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.footer--raised {
|
|
60
|
+
box-shadow: 0 calc(var(--ui-border-width-sm) * -1) 6px hsl(var(--ui-hue-primary, 220) 10% 20% / 0.08);
|
|
61
|
+
}
|
|
62
|
+
}
|
package/src/03-layout/index.scss
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
@forward './app-shell';
|
|
2
|
+
@forward './aspect-ratio';
|
|
1
3
|
@forward './box';
|
|
2
|
-
@forward './
|
|
4
|
+
@forward './center';
|
|
3
5
|
@forward './cluster';
|
|
6
|
+
@forward './container';
|
|
7
|
+
@forward './content';
|
|
4
8
|
@forward './flex';
|
|
5
|
-
@forward './
|
|
9
|
+
@forward './footer';
|
|
6
10
|
@forward './grid';
|
|
7
|
-
@forward './
|
|
11
|
+
@forward './main';
|
|
12
|
+
@forward './nav-rail';
|
|
13
|
+
@forward './page-header';
|
|
8
14
|
@forward './sidebar';
|
|
9
15
|
@forward './sidebar-nav';
|
|
10
|
-
@forward './
|
|
11
|
-
@forward './
|
|
16
|
+
@forward './stack';
|
|
17
|
+
@forward './topbar';
|
|
@@ -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
|
+
}
|