@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.
Files changed (72) hide show
  1. package/dist/compiled.css +1 -1
  2. package/dist/index.css +23 -7
  3. package/package.json +1 -1
  4. package/src/03-layout/app-shell/app-shell.api.json +18 -0
  5. package/src/03-layout/app-shell/app-shell.docs.json +18 -0
  6. package/src/03-layout/aspect-ratio/aspect-ratio-visual.png +0 -0
  7. package/src/03-layout/aspect-ratio/aspect-ratio.api.json +16 -0
  8. package/src/03-layout/aspect-ratio/aspect-ratio.docs.json +193 -0
  9. package/src/03-layout/aspect-ratio/aspect-ratio.visual.spec.ts +14 -0
  10. package/src/03-layout/aspect-ratio/index.scss +23 -0
  11. package/src/03-layout/content/content-visual.png +0 -0
  12. package/src/03-layout/content/content.api.json +26 -0
  13. package/src/03-layout/content/content.docs.json +82 -0
  14. package/src/03-layout/content/content.visual.spec.ts +14 -0
  15. package/src/03-layout/content/index.scss +25 -0
  16. package/src/03-layout/footer/footer-visual.png +0 -0
  17. package/src/03-layout/footer/footer.api.json +45 -0
  18. package/src/03-layout/footer/footer.docs.json +110 -0
  19. package/src/03-layout/footer/footer.visual.spec.ts +14 -0
  20. package/src/03-layout/footer/index.scss +62 -0
  21. package/src/03-layout/index.scss +11 -5
  22. package/src/03-layout/main/index.scss +1 -0
  23. package/src/03-layout/nav-rail/index.scss +50 -0
  24. package/src/03-layout/nav-rail/nav-rail-visual.png +0 -0
  25. package/src/03-layout/nav-rail/nav-rail.api.json +35 -0
  26. package/src/03-layout/nav-rail/nav-rail.docs.json +76 -0
  27. package/src/03-layout/nav-rail/nav-rail.visual.spec.ts +14 -0
  28. package/src/03-layout/page-header/index.scss +52 -0
  29. package/src/03-layout/page-header/page-header-visual.png +0 -0
  30. package/src/03-layout/page-header/page-header.api.json +35 -0
  31. package/src/03-layout/page-header/page-header.docs.json +124 -0
  32. package/src/03-layout/page-header/page-header.visual.spec.ts +14 -0
  33. package/src/03-layout/sidebar/index.scss +2 -2
  34. package/src/03-layout/topbar/index.scss +62 -0
  35. package/src/03-layout/topbar/topbar-visual.png +0 -0
  36. package/src/03-layout/topbar/topbar.api.json +45 -0
  37. package/src/03-layout/topbar/topbar.docs.json +110 -0
  38. package/src/03-layout/topbar/topbar.visual.spec.ts +14 -0
  39. package/src/04-components/actions/button-group/button-group-visual.png +0 -0
  40. package/src/04-components/actions/button-group/button-group.visual.spec.ts +1 -2
  41. package/src/04-components/actions/button-group/index.scss +3 -1
  42. package/src/04-components/data-display/avatar/avatar-visual.png +0 -0
  43. package/src/04-components/data-display/stat/index.scss +47 -0
  44. package/src/04-components/data-display/stat/stat-visual.png +0 -0
  45. package/src/04-components/data-display/stat/stat.api.json +18 -0
  46. package/src/04-components/data-display/stat/stat.docs.json +114 -0
  47. package/src/04-components/data-display/stat/stat.visual.spec.ts +14 -0
  48. package/src/04-components/disclosure/accordion/accordion-visual.png +0 -0
  49. package/src/04-components/disclosure/disclosure/disclosure-visual.png +0 -0
  50. package/src/04-components/forms/field/field-visual.png +0 -0
  51. package/src/04-components/forms/input/input-visual.png +0 -0
  52. package/src/04-components/index.scss +5 -0
  53. package/src/04-components/layout/spacer/index.scss +11 -0
  54. package/src/04-components/layout/spacer/spacer-visual.png +0 -0
  55. package/src/04-components/layout/spacer/spacer.api.json +6 -0
  56. package/src/04-components/layout/spacer/spacer.docs.json +102 -0
  57. package/src/04-components/layout/spacer/spacer.visual.spec.ts +14 -0
  58. package/src/04-components/typography/blockquote/blockquote-visual.png +0 -0
  59. package/src/04-components/typography/blockquote/blockquote.api.json +18 -0
  60. package/src/04-components/typography/blockquote/blockquote.docs.json +89 -0
  61. package/src/04-components/typography/blockquote/blockquote.visual.spec.ts +14 -0
  62. package/src/04-components/typography/blockquote/index.scss +41 -0
  63. package/src/04-components/typography/kbd/index.scss +33 -0
  64. package/src/04-components/typography/kbd/kbd-visual.png +0 -0
  65. package/src/04-components/typography/kbd/kbd.api.json +15 -0
  66. package/src/04-components/typography/kbd/kbd.docs.json +61 -0
  67. package/src/04-components/typography/kbd/kbd.visual.spec.ts +14 -0
  68. package/src/04-components/typography/mark/index.scss +22 -0
  69. package/src/04-components/typography/mark/mark-visual.png +0 -0
  70. package/src/04-components/typography/mark/mark.api.json +12 -0
  71. package/src/04-components/typography/mark/mark.docs.json +60 -0
  72. package/src/04-components/typography/mark/mark.visual.spec.ts +14 -0
@@ -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
+ }
@@ -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
+ });
@@ -7,8 +7,7 @@ const DOCS_PATH = resolve(__dirname, 'button-group.docs.json');
7
7
  test.describe('button-group visual regression', () => {
8
8
  test('all variations', async ({ page }) => {
9
9
  await setupVisualTestFromDocs(page, DOCS_PATH);
10
- // TODO: fix grid rhythm - negative margins cause off-grid heights (#178)
11
- // await validateGridRhythm(page, 'button-group');
10
+ await validateGridRhythm(page, 'button-group');
12
11
  await saveForLostPixel(page, 'button-group');
13
12
  await expect(page.locator('body')).toHaveScreenshot('button-group.visual.png');
14
13
  });
@@ -59,7 +59,9 @@
59
59
  }
60
60
 
61
61
  .button-group--vertical > .button + .button {
62
- margin-block-start: calc(#{t.$unit} / -8);
62
+ // Remove top border instead of negative margin to stay on 8px grid
63
+ border-block-start-width: 0;
64
+
63
65
  margin-inline-start: 0;
64
66
  }
65
67
 
@@ -0,0 +1,47 @@
1
+ @use '../../../00-config/tokens/variables' as t;
2
+
3
+ @layer components.tokens {
4
+ .stat {
5
+ --_gap: var(--ui-stat-gap, var(--ui-space-1, #{t.$space-1}));
6
+ }
7
+
8
+ .stat__value {
9
+ --_font-size: var(--ui-stat-value-font-size, var(--ui-font-size-3xl, #{t.$font-size-3xl}));
10
+ --_font-weight: var(--ui-stat-value-font-weight, var(--ui-weight-bold, #{t.$weight-bold}));
11
+ --_line-height: var(--ui-stat-value-line-height, var(--ui-leading-3xl, #{t.$leading-3xl}));
12
+ --_color: var(--ui-stat-value-color, var(--ui-color-text, #{t.$color-text}));
13
+ }
14
+
15
+ .stat__label {
16
+ --_font-size: var(--ui-stat-label-font-size, var(--ui-font-size-sm, #{t.$font-size-sm}));
17
+ --_color: var(--ui-stat-label-color, var(--ui-color-text-muted, #{t.$color-text-muted}));
18
+ --_line-height: var(--ui-stat-label-line-height, var(--ui-leading-sm, #{t.$leading-sm}));
19
+ }
20
+
21
+ .stat--sm .stat__value {
22
+ --_font-size: var(--ui-font-size-xl, #{t.$font-size-xl});
23
+ --_line-height: var(--ui-leading-xl, #{t.$leading-xl});
24
+ }
25
+ }
26
+
27
+ @layer components.styles {
28
+ .stat {
29
+ display: flex;
30
+ flex-direction: column;
31
+ gap: var(--_gap);
32
+ }
33
+
34
+ .stat__value {
35
+ font-size: var(--_font-size);
36
+ font-weight: var(--_font-weight);
37
+ line-height: var(--_line-height);
38
+ letter-spacing: var(--ui-tracking-display, #{t.$tracking-display});
39
+ color: var(--_color);
40
+ }
41
+
42
+ .stat__label {
43
+ font-size: var(--_font-size);
44
+ line-height: var(--_line-height);
45
+ color: var(--_color);
46
+ }
47
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "stat",
3
+ "element": "div",
4
+ "modifiers": {
5
+ "size": {
6
+ "values": ["sm"]
7
+ }
8
+ },
9
+ "subElements": ["value", "label"],
10
+ "cssVars": [
11
+ { "name": "--ui-stat-gap", "default": "var(--ui-space-1)" },
12
+ { "name": "--ui-stat-value-font-size", "default": "var(--ui-font-size-3xl)" },
13
+ { "name": "--ui-stat-value-font-weight", "default": "var(--ui-weight-bold)" },
14
+ { "name": "--ui-stat-value-color", "default": "var(--ui-color-text)" },
15
+ { "name": "--ui-stat-label-font-size", "default": "var(--ui-font-size-sm)" },
16
+ { "name": "--ui-stat-label-color", "default": "var(--ui-color-text-muted)" }
17
+ ]
18
+ }
@@ -0,0 +1,114 @@
1
+ {
2
+ "id": "stat",
3
+ "type": "component",
4
+ "title": "Stat",
5
+ "description": "Statistic display with prominent value and descriptive label.",
6
+ "api": "./stat.api.json",
7
+ "sections": [
8
+ {
9
+ "title": "Default",
10
+ "examples": [
11
+ {
12
+ "items": [
13
+ {
14
+ "tag": "div",
15
+ "class": "ui-stat",
16
+ "children": [
17
+ { "tag": "span", "class": "ui-stat__value", "text": "2,847" },
18
+ { "tag": "span", "class": "ui-stat__label", "text": "Total users" }
19
+ ]
20
+ }
21
+ ]
22
+ }
23
+ ]
24
+ },
25
+ {
26
+ "title": "Multiple stats",
27
+ "examples": [
28
+ {
29
+ "items": [
30
+ {
31
+ "tag": "div",
32
+ "class": "ui-cluster ui-cluster--lg",
33
+ "children": [
34
+ {
35
+ "tag": "div",
36
+ "class": "ui-stat",
37
+ "children": [
38
+ { "tag": "span", "class": "ui-stat__value", "text": "12.5k" },
39
+ { "tag": "span", "class": "ui-stat__label", "text": "Revenue" }
40
+ ]
41
+ },
42
+ {
43
+ "tag": "div",
44
+ "class": "ui-stat",
45
+ "children": [
46
+ { "tag": "span", "class": "ui-stat__value", "text": "573" },
47
+ { "tag": "span", "class": "ui-stat__label", "text": "Orders" }
48
+ ]
49
+ },
50
+ {
51
+ "tag": "div",
52
+ "class": "ui-stat",
53
+ "children": [
54
+ { "tag": "span", "class": "ui-stat__value", "text": "98%" },
55
+ { "tag": "span", "class": "ui-stat__label", "text": "Satisfaction" }
56
+ ]
57
+ }
58
+ ]
59
+ }
60
+ ]
61
+ }
62
+ ]
63
+ },
64
+ {
65
+ "title": "Small",
66
+ "examples": [
67
+ {
68
+ "items": [
69
+ {
70
+ "tag": "div",
71
+ "class": "ui-stat ui-stat--sm",
72
+ "children": [
73
+ { "tag": "span", "class": "ui-stat__value", "text": "42" },
74
+ { "tag": "span", "class": "ui-stat__label", "text": "Active now" }
75
+ ]
76
+ }
77
+ ]
78
+ }
79
+ ]
80
+ }
81
+ ],
82
+ "customization": [
83
+ {
84
+ "token": "--ui-stat-gap",
85
+ "default": "var(--ui-space-1)",
86
+ "description": "Gap between value and label"
87
+ },
88
+ {
89
+ "token": "--ui-stat-value-font-size",
90
+ "default": "var(--ui-font-size-3xl)",
91
+ "description": "Value font size"
92
+ },
93
+ {
94
+ "token": "--ui-stat-value-font-weight",
95
+ "default": "var(--ui-weight-bold)",
96
+ "description": "Value font weight"
97
+ },
98
+ {
99
+ "token": "--ui-stat-value-color",
100
+ "default": "var(--ui-color-text)",
101
+ "description": "Value text color"
102
+ },
103
+ {
104
+ "token": "--ui-stat-label-font-size",
105
+ "default": "var(--ui-font-size-sm)",
106
+ "description": "Label font size"
107
+ },
108
+ {
109
+ "token": "--ui-stat-label-color",
110
+ "default": "var(--ui-color-text-muted)",
111
+ "description": "Label text color"
112
+ }
113
+ ]
114
+ }
@@ -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, 'stat.docs.json');
6
+
7
+ test.describe('stat visual regression', () => {
8
+ test('all variations', async ({ page }) => {
9
+ await setupVisualTestFromDocs(page, DOCS_PATH);
10
+ await validateGridRhythm(page, 'stat');
11
+ await saveForLostPixel(page, 'stat');
12
+ await expect(page.locator('body')).toHaveScreenshot('stat.visual.png');
13
+ });
14
+ });
@@ -5,6 +5,7 @@
5
5
  @forward './data-display/card/index';
6
6
  @forward './data-display/data-list/index';
7
7
  @forward './data-display/icon/index';
8
+ @forward './data-display/stat/index';
8
9
  @forward './data-display/status/index';
9
10
  @forward './data-display/table/index';
10
11
  @forward './data-display/tag/index';
@@ -26,6 +27,7 @@
26
27
  @forward './forms/textarea/index';
27
28
  @forward './forms/toggle/index';
28
29
  @forward './layout/divider/index';
30
+ @forward './layout/spacer/index';
29
31
  @forward './navigation/breadcrumb/index';
30
32
  @forward './navigation/menu/index';
31
33
  @forward './navigation/nav/index';
@@ -37,6 +39,9 @@
37
39
  @forward './overlays/overlay/index';
38
40
  @forward './overlays/popover/index';
39
41
  @forward './overlays/tooltip/index';
42
+ @forward './typography/blockquote/index';
40
43
  @forward './typography/code/index';
41
44
  @forward './typography/heading/index';
45
+ @forward './typography/kbd/index';
42
46
  @forward './typography/link/index';
47
+ @forward './typography/mark/index';
@@ -0,0 +1,11 @@
1
+ @layer components.tokens {
2
+ .spacer {
3
+ --_size: var(--ui-spacer-size, 1);
4
+ }
5
+ }
6
+
7
+ @layer components.styles {
8
+ .spacer {
9
+ flex: var(--_size);
10
+ }
11
+ }