@teseor/css 1.14.2 → 1.14.3

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 (92) hide show
  1. package/package.json +1 -1
  2. package/src/base/root.docs.json +35 -0
  3. package/src/components/actions/button/button.visual.spec.ts +5 -1
  4. package/src/components/actions/button-group/button-group.visual.spec.ts +5 -1
  5. package/src/components/actions/close-button/close-button.visual.spec.ts +5 -1
  6. package/src/components/content/divider/divider.visual.spec.ts +5 -1
  7. package/src/components/content/scroll-area/scroll-area.visual.spec.ts +5 -1
  8. package/src/components/content/spacer/spacer.visual.spec.ts +1 -1
  9. package/src/components/data-display/avatar/avatar.visual.spec.ts +5 -1
  10. package/src/components/data-display/badge/badge.visual.spec.ts +5 -1
  11. package/src/components/data-display/card/card.visual.spec.ts +5 -1
  12. package/src/components/data-display/data-list/data-list.visual.spec.ts +5 -1
  13. package/src/components/data-display/icon/icon.visual.spec.ts +5 -1
  14. package/src/components/data-display/image/image.visual.spec.ts +5 -1
  15. package/src/components/data-display/stat/stat.visual.spec.ts +5 -1
  16. package/src/components/data-display/status/status.visual.spec.ts +5 -1
  17. package/src/components/data-display/table/table.visual.spec.ts +5 -1
  18. package/src/components/data-display/tag/tag.visual.spec.ts +5 -1
  19. package/src/components/disclosure/accordion/accordion.visual.spec.ts +5 -1
  20. package/src/components/disclosure/disclosure/disclosure.visual.spec.ts +5 -1
  21. package/src/components/feedback/alert/alert.visual.spec.ts +5 -1
  22. package/src/components/feedback/progress/progress.visual.spec.ts +5 -1
  23. package/src/components/feedback/progress-circle/progress-circle.visual.spec.ts +5 -1
  24. package/src/components/feedback/skeleton/skeleton.visual.spec.ts +5 -1
  25. package/src/components/feedback/spinner/spinner.visual.spec.ts +5 -1
  26. package/src/components/feedback/toast/toast.visual.spec.ts +5 -1
  27. package/src/components/forms/checkbox/checkbox.visual.spec.ts +5 -1
  28. package/src/components/forms/checkbox-group/checkbox-group.visual.spec.ts +5 -1
  29. package/src/components/forms/field/field.visual.spec.ts +5 -1
  30. package/src/components/forms/fieldset/fieldset.visual.spec.ts +5 -1
  31. package/src/components/forms/form/form.visual.spec.ts +5 -1
  32. package/src/components/forms/form-error/form-error.visual.spec.ts +5 -1
  33. package/src/components/forms/form-helper/form-helper.visual.spec.ts +5 -1
  34. package/src/components/forms/input/input.visual.spec.ts +5 -1
  35. package/src/components/forms/label/label.visual.spec.ts +5 -1
  36. package/src/components/forms/number-input/number-input.visual.spec.ts +5 -1
  37. package/src/components/forms/password-input/password-input.visual.spec.ts +5 -1
  38. package/src/components/forms/radio/radio.visual.spec.ts +5 -1
  39. package/src/components/forms/radio-group/radio-group.visual.spec.ts +5 -1
  40. package/src/components/forms/search-input/search-input.visual.spec.ts +5 -1
  41. package/src/components/forms/select/select.visual.spec.ts +5 -1
  42. package/src/components/forms/slider/slider.visual.spec.ts +5 -1
  43. package/src/components/forms/textarea/textarea.visual.spec.ts +5 -1
  44. package/src/components/forms/toggle/toggle.visual.spec.ts +5 -1
  45. package/src/components/navigation/breadcrumb/breadcrumb.visual.spec.ts +5 -1
  46. package/src/components/navigation/dropdown-menu/dropdown-menu.visual.spec.ts +5 -1
  47. package/src/components/navigation/menu/menu.visual.spec.ts +5 -1
  48. package/src/components/navigation/nav/nav.visual.spec.ts +5 -1
  49. package/src/components/navigation/pagination/pagination.visual.spec.ts +5 -1
  50. package/src/components/navigation/tabs/tabs.visual.spec.ts +5 -1
  51. package/src/components/overlays/dialog/dialog.visual.spec.ts +5 -1
  52. package/src/components/overlays/drawer/drawer.visual.spec.ts +5 -1
  53. package/src/components/overlays/modal/modal.visual.spec.ts +5 -1
  54. package/src/components/overlays/overlay/overlay.visual.spec.ts +5 -1
  55. package/src/components/overlays/popover/popover.visual.spec.ts +5 -1
  56. package/src/components/overlays/tooltip/tooltip.visual.spec.ts +5 -1
  57. package/src/components/typography/blockquote/blockquote.visual.spec.ts +5 -1
  58. package/src/components/typography/code/code.visual.spec.ts +5 -1
  59. package/src/components/typography/code-block/code-block.visual.spec.ts +5 -1
  60. package/src/components/typography/heading/heading.visual.spec.ts +5 -1
  61. package/src/components/typography/kbd/kbd.visual.spec.ts +5 -1
  62. package/src/components/typography/link/link.visual.spec.ts +5 -1
  63. package/src/components/typography/list/list.visual.spec.ts +5 -1
  64. package/src/components/typography/mark/mark.visual.spec.ts +5 -1
  65. package/src/config/{tokens → guides}/accessibility.docs.json +1 -0
  66. package/src/config/guides/getting-started.docs.json +106 -0
  67. package/src/config/{tokens → guides}/theming.docs.json +1 -0
  68. package/src/config/tokens/design-tokens.docs.json +239 -0
  69. package/src/debug/debug.docs.json +96 -0
  70. package/src/debug/index.scss +81 -1
  71. package/src/layout/app-shell/app-shell.visual.spec.ts +1 -1
  72. package/src/layout/aspect-ratio/aspect-ratio.visual.spec.ts +1 -1
  73. package/src/layout/box/box.visual.spec.ts +1 -1
  74. package/src/layout/center/center.visual.spec.ts +1 -1
  75. package/src/layout/column/column.visual.spec.ts +1 -1
  76. package/src/layout/content/content.visual.spec.ts +1 -1
  77. package/src/layout/footer/footer.visual.spec.ts +1 -1
  78. package/src/layout/grid/grid.visual.spec.ts +1 -1
  79. package/src/layout/nav-rail/nav-rail.visual.spec.ts +1 -1
  80. package/src/layout/page-header/page-header.visual.spec.ts +1 -1
  81. package/src/layout/row/row.visual.spec.ts +1 -1
  82. package/src/layout/sidebar/sidebar.visual.spec.ts +1 -1
  83. package/src/layout/sidebar-nav/sidebar-nav.visual.spec.ts +1 -1
  84. package/src/layout/topbar/topbar.visual.spec.ts +1 -1
  85. package/src/debug/grid-overlay.scss +0 -81
  86. package/src/testing/api-types.ts +0 -20
  87. package/src/testing/grid-alignment.spec.ts +0 -38
  88. package/src/testing/html-generator.ts +0 -151
  89. package/src/testing/index.ts +0 -15
  90. package/src/testing/page-setup.ts +0 -149
  91. package/src/testing/rhythm.ts +0 -146
  92. package/src/testing/scaffold.ts +0 -50
@@ -1 +1,81 @@
1
- @forward "./grid-overlay";
1
+ @use '../config/tokens/variables' as t;
2
+ // Debug grid overlay - add class="debug-grid" to body or any container
3
+ .debug-grid,
4
+ .debug-grid-rows,
5
+ .debug-baseline {
6
+ position: relative;
7
+ }
8
+
9
+ .debug-grid {
10
+ // Fallback hue 220 (blue) when --ui-hue-primary not set
11
+ --debug-color: hsl(var(--ui-hue-primary, #{t.$hue-primary}) 80% 50% / 0.15);
12
+ }
13
+
14
+ .debug-grid::after {
15
+ content: "";
16
+ position: absolute;
17
+ inset-block-start: 0;
18
+ inset-inline-start: 0;
19
+ z-index: var(--ui-z-debug);
20
+
21
+ block-size: 100%;
22
+ inline-size: 100%;
23
+ min-block-size: 100vh;
24
+
25
+ background-image: linear-gradient(to right, var(--debug-color) 1px, transparent 1px),
26
+ linear-gradient(to bottom, var(--debug-color) 1px, transparent 1px);
27
+ pointer-events: none;
28
+ background-size: var(--ui-unit) var(--ui-unit);
29
+ background-position: 0 0;
30
+ }
31
+
32
+ // Stronger grid at row intervals
33
+ .debug-grid-rows {
34
+ --debug-color: hsl(var(--ui-hue-primary, #{t.$hue-primary}) 80% 50% / 0.1);
35
+ --debug-color-strong: hsl(var(--ui-hue-primary, #{t.$hue-primary}) 80% 50% / 0.25);
36
+ }
37
+
38
+ .debug-grid-rows::after {
39
+ content: "";
40
+ position: absolute;
41
+ inset-block-start: 0;
42
+ inset-inline-start: 0;
43
+ z-index: var(--ui-z-debug);
44
+
45
+ block-size: 100%;
46
+ inline-size: 100%;
47
+ min-block-size: 100vh;
48
+
49
+ background-image: linear-gradient(to right, var(--debug-color) 1px, transparent 1px),
50
+ linear-gradient(to bottom, var(--debug-color) 1px, transparent 1px),
51
+ linear-gradient(to bottom, var(--debug-color-strong) 1px, transparent 1px);
52
+ pointer-events: none;
53
+ background-size: var(--ui-unit) var(--ui-unit), var(--ui-unit) var(--ui-unit), var(--ui-unit) var(--ui-row);
54
+ background-position: 0 0;
55
+ }
56
+
57
+ // Baseline grid only (horizontal lines)
58
+ .debug-baseline {
59
+ --debug-color: hsl(var(--ui-hue-danger, 0) 80% 50% / 0.2);
60
+ }
61
+
62
+ .debug-baseline::after {
63
+ content: "";
64
+ position: absolute;
65
+ inset-block-start: 0;
66
+ inset-inline-start: 0;
67
+ z-index: var(--ui-z-debug);
68
+
69
+ block-size: 100%;
70
+ inline-size: 100%;
71
+ min-block-size: 100vh;
72
+
73
+ background-image: linear-gradient(to bottom, var(--debug-color) 1px, transparent 1px);
74
+ pointer-events: none;
75
+ background-size: 100% var(--ui-unit);
76
+ }
77
+
78
+ // Element boundaries outline
79
+ .debug-outline * {
80
+ outline: 1px solid hsl(0 100% 50% / 0.5);
81
+ }
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'app-shell.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'aspect-ratio.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'box.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'center.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'column.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'content.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'footer.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'grid.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'nav-rail.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'page-header.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'row.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'sidebar.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'sidebar-nav.docs.json');
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { expect, test } from '@playwright/test';
3
- import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../testing';
3
+ import { saveForLostPixel, setupVisualTestFromDocs, validateGridRhythm } from '../../../test-utils';
4
4
 
5
5
  const DOCS_PATH = resolve(__dirname, 'topbar.docs.json');
6
6
 
@@ -1,81 +0,0 @@
1
- @use '../config/tokens/variables' as t;
2
- // Debug grid overlay - add class="debug-grid" to body or any container
3
- .debug-grid,
4
- .debug-grid-rows,
5
- .debug-baseline {
6
- position: relative;
7
- }
8
-
9
- .debug-grid {
10
- // Fallback hue 220 (blue) when --ui-hue-primary not set
11
- --debug-color: hsl(var(--ui-hue-primary, #{t.$hue-primary}) 80% 50% / 0.15);
12
- }
13
-
14
- .debug-grid::after {
15
- content: "";
16
- position: absolute;
17
- inset-block-start: 0;
18
- inset-inline-start: 0;
19
- z-index: var(--ui-z-debug);
20
-
21
- block-size: 100%;
22
- inline-size: 100%;
23
- min-block-size: 100vh;
24
-
25
- background-image: linear-gradient(to right, var(--debug-color) 1px, transparent 1px),
26
- linear-gradient(to bottom, var(--debug-color) 1px, transparent 1px);
27
- pointer-events: none;
28
- background-size: var(--ui-unit) var(--ui-unit);
29
- background-position: 0 0;
30
- }
31
-
32
- // Stronger grid at row intervals
33
- .debug-grid-rows {
34
- --debug-color: hsl(var(--ui-hue-primary, #{t.$hue-primary}) 80% 50% / 0.1);
35
- --debug-color-strong: hsl(var(--ui-hue-primary, #{t.$hue-primary}) 80% 50% / 0.25);
36
- }
37
-
38
- .debug-grid-rows::after {
39
- content: "";
40
- position: absolute;
41
- inset-block-start: 0;
42
- inset-inline-start: 0;
43
- z-index: var(--ui-z-debug);
44
-
45
- block-size: 100%;
46
- inline-size: 100%;
47
- min-block-size: 100vh;
48
-
49
- background-image: linear-gradient(to right, var(--debug-color) 1px, transparent 1px),
50
- linear-gradient(to bottom, var(--debug-color) 1px, transparent 1px),
51
- linear-gradient(to bottom, var(--debug-color-strong) 1px, transparent 1px);
52
- pointer-events: none;
53
- background-size: var(--ui-unit) var(--ui-unit), var(--ui-unit) var(--ui-unit), var(--ui-unit) var(--ui-row);
54
- background-position: 0 0;
55
- }
56
-
57
- // Baseline grid only (horizontal lines)
58
- .debug-baseline {
59
- --debug-color: hsl(var(--ui-hue-danger, 0) 80% 50% / 0.2);
60
- }
61
-
62
- .debug-baseline::after {
63
- content: "";
64
- position: absolute;
65
- inset-block-start: 0;
66
- inset-inline-start: 0;
67
- z-index: var(--ui-z-debug);
68
-
69
- block-size: 100%;
70
- inline-size: 100%;
71
- min-block-size: 100vh;
72
-
73
- background-image: linear-gradient(to bottom, var(--debug-color) 1px, transparent 1px);
74
- pointer-events: none;
75
- background-size: 100% var(--ui-unit);
76
- }
77
-
78
- // Element boundaries outline
79
- .debug-outline * {
80
- outline: 1px solid hsl(0 100% 50% / 0.5);
81
- }
@@ -1,20 +0,0 @@
1
- export interface ComponentAPI {
2
- name: string;
3
- baseClass?: string;
4
- element: string;
5
- description: string;
6
- modifiers: Record<
7
- string,
8
- {
9
- values?: string[];
10
- type?: string;
11
- default?: string | null;
12
- description: string;
13
- }
14
- >;
15
- states?: string[];
16
- combinations?: Array<{
17
- modifiers: string[];
18
- disabled?: boolean;
19
- }>;
20
- }
@@ -1,38 +0,0 @@
1
- import { readdirSync, statSync } from 'node:fs';
2
- import { basename, join, resolve } from 'node:path';
3
- import { test } from '@playwright/test';
4
- import { generateHtmlFromDocs, loadDocsJson, setupVisualTest, validateGridRhythm } from '.';
5
-
6
- const COMPONENTS_DIR = resolve(__dirname, '../components');
7
-
8
- // Known grid rhythm issues tracked in separate issues
9
- const SKIP_COMPONENTS = [
10
- 'data-list', // 1px borders cause off-grid heights (#179)
11
- 'spinner', // CSS var resolution issue (#154)
12
- 'spacer', // no intrinsic height by design
13
- ];
14
-
15
- function findDocsFiles(dir: string): Array<{ name: string; path: string }> {
16
- const results: Array<{ name: string; path: string }> = [];
17
- for (const entry of readdirSync(dir)) {
18
- const full = join(dir, entry);
19
- if (statSync(full).isDirectory()) {
20
- results.push(...findDocsFiles(full));
21
- } else if (entry.endsWith('.docs.json')) {
22
- results.push({ name: basename(entry, '.docs.json'), path: full });
23
- }
24
- }
25
- return results;
26
- }
27
-
28
- const docsFiles = findDocsFiles(COMPONENTS_DIR);
29
-
30
- for (const { name, path } of docsFiles) {
31
- test(`${name} aligns to vertical grid`, async ({ page }) => {
32
- test.skip(SKIP_COMPONENTS.includes(name), `${name} has known grid issues`);
33
- const docs = loadDocsJson(path);
34
- const html = generateHtmlFromDocs(docs);
35
- await setupVisualTest(page, { html });
36
- await validateGridRhythm(page, name);
37
- });
38
- }
@@ -1,151 +0,0 @@
1
- import type { ComponentAPI } from './api-types';
2
-
3
- const PREFIX = 'ui-';
4
-
5
- function generateCombinations(
6
- api: ComponentAPI,
7
- ): Array<{ modifiers: string[]; disabled?: boolean }> {
8
- if (api.combinations) return api.combinations;
9
-
10
- const enumModifiers = Object.entries(api.modifiers)
11
- .filter(([, def]) => def.values)
12
- .map(([, def]) => def.values!);
13
-
14
- if (enumModifiers.length < 2) return [];
15
-
16
- const [first, second] = enumModifiers;
17
- const combos: Array<{ modifiers: string[] }> = [];
18
-
19
- for (const a of first) {
20
- for (const b of second) {
21
- combos.push({ modifiers: [a, b] });
22
- }
23
- }
24
-
25
- return combos;
26
- }
27
-
28
- function capitalize(s: string): string {
29
- return s.charAt(0).toUpperCase() + s.slice(1);
30
- }
31
-
32
- export function generateVariationsHtml(api: ComponentAPI): string {
33
- const baseClass = `${PREFIX}${api.baseClass ?? api.name}`;
34
- const element = api.element;
35
- const title = capitalize(api.name);
36
-
37
- const sections: string[] = [];
38
-
39
- // Default section
40
- sections.push(`
41
- <div class="test-section">
42
- <div class="test-section-title">Default</div>
43
- <div class="test-row">
44
- <${element} class="${baseClass}">Default</${element}>
45
- </div>
46
- </div>
47
- `);
48
-
49
- // Generate sections for each modifier
50
- for (const [modName, modDef] of Object.entries(api.modifiers)) {
51
- const sectionTitle = capitalize(modName);
52
-
53
- if (modDef.values) {
54
- const buttons: string[] = [];
55
-
56
- if (modName === 'size') {
57
- buttons.push(`<${element} class="${baseClass}">Default</${element}>`);
58
- }
59
- if (modName === 'variant') {
60
- buttons.push(`<${element} class="${baseClass}">Primary</${element}>`);
61
- }
62
-
63
- for (const value of modDef.values) {
64
- const modClass = `${baseClass}--${value}`;
65
- const label = capitalize(value);
66
- buttons.push(`<${element} class="${baseClass} ${modClass}">${label}</${element}>`);
67
- }
68
-
69
- sections.push(`
70
- <div class="test-section">
71
- <div class="test-section-title">${sectionTitle}</div>
72
- <div class="test-row">
73
- ${buttons.join('\n ')}
74
- </div>
75
- </div>
76
- `);
77
- } else if (modDef.type === 'boolean') {
78
- const modClass = `${baseClass}--${modName}`;
79
- const content = modName === 'icon' ? '+' : 'Full Width';
80
- const blockClass = modName === 'block' ? ' test-element--block' : '';
81
-
82
- sections.push(`
83
- <div class="test-section">
84
- <div class="test-section-title">${sectionTitle}</div>
85
- <div class="test-row">
86
- <div class="${blockClass.trim()}">
87
- <${element} class="${baseClass} ${modClass}">${content}</${element}>
88
- </div>
89
- </div>
90
- </div>
91
- `);
92
- }
93
- }
94
-
95
- // States section
96
- if (api.states && api.states.length > 0) {
97
- const stateButtons: string[] = [];
98
- stateButtons.push(`<${element} class="${baseClass}">Normal</${element}>`);
99
-
100
- if (api.states.includes('hover')) {
101
- stateButtons.push(`<${element} class="${baseClass} ${baseClass}--hover">Hover</${element}>`);
102
- }
103
- if (api.states.includes('focus')) {
104
- stateButtons.push(`<${element} class="${baseClass} ${baseClass}--focus">Focus</${element}>`);
105
- }
106
- if (api.states.includes('active')) {
107
- stateButtons.push(
108
- `<${element} class="${baseClass} ${baseClass}--active">Active</${element}>`,
109
- );
110
- }
111
- if (api.states.includes('disabled')) {
112
- stateButtons.push(`<${element} class="${baseClass}" disabled>Disabled</${element}>`);
113
- }
114
-
115
- sections.push(`
116
- <div class="test-section">
117
- <div class="test-section-title">States</div>
118
- <div class="test-row">
119
- ${stateButtons.join('\n ')}
120
- </div>
121
- </div>
122
- `);
123
- }
124
-
125
- // Combinations section
126
- const combos = generateCombinations(api);
127
- if (combos.length > 0) {
128
- const comboButtons = combos.map((combo) => {
129
- const classes = combo.modifiers.map((m) => `${baseClass}--${m}`).join(' ');
130
- const label = combo.modifiers.map((m) => capitalize(m)).join(' ');
131
- const disabled = combo.disabled ? ' disabled' : '';
132
- return `<${element} class="${baseClass} ${classes}"${disabled}>${label}</${element}>`;
133
- });
134
-
135
- sections.push(`
136
- <div class="test-section">
137
- <div class="test-section-title">Combinations</div>
138
- <div class="test-grid">
139
- ${comboButtons.join('\n ')}
140
- </div>
141
- </div>
142
- `);
143
- }
144
-
145
- return `
146
- <div class="test-container">
147
- <h1 class="test-title">${title}</h1>
148
- ${sections.join('')}
149
- </div>
150
- `;
151
- }
@@ -1,15 +0,0 @@
1
- export type { ComponentAPI } from './api-types';
2
- export type { RhythmViolation } from './rhythm';
3
- export { scaffoldCss } from './scaffold';
4
- export { generateVariationsHtml } from './html-generator';
5
- export { validateGridRhythm } from './rhythm';
6
- export {
7
- loadCss,
8
- loadComponentApi,
9
- loadDocsJson,
10
- generateHtmlFromDocs,
11
- setupVisualTest,
12
- setupVisualTestFromApi,
13
- setupVisualTestFromDocs,
14
- saveForLostPixel,
15
- } from './page-setup';
@@ -1,149 +0,0 @@
1
- import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
- import { resolve } from 'node:path';
3
- import type { Page } from '@playwright/test';
4
-
5
- const LOSTPIXEL_DIR = resolve(__dirname, '../../.lostpixel');
6
- import type { ComponentAPI } from './api-types';
7
- import { generateVariationsHtml } from './html-generator';
8
- import { scaffoldCss } from './scaffold';
9
-
10
- interface DocsItem {
11
- tag?: string;
12
- class?: string;
13
- text?: string;
14
- attrs?: Record<string, string>;
15
- style?: Record<string, string>;
16
- children?: DocsItem[];
17
- }
18
-
19
- interface DocsExample {
20
- html?: string;
21
- items?: DocsItem[];
22
- layout?: string;
23
- title?: string;
24
- }
25
-
26
- interface DocsSection {
27
- title: string;
28
- examples: DocsExample[];
29
- }
30
-
31
- interface DocsJson {
32
- sections: DocsSection[];
33
- }
34
-
35
- const DIST_PATH = resolve(__dirname, '../../dist');
36
-
37
- export function loadCss(filename = 'index.css'): string {
38
- return readFileSync(resolve(DIST_PATH, filename), 'utf-8');
39
- }
40
-
41
- export function loadComponentApi(apiPath: string): ComponentAPI {
42
- return JSON.parse(readFileSync(apiPath, 'utf-8'));
43
- }
44
-
45
- function renderItem(item: DocsItem): string {
46
- if (!item.tag) return item.text ?? '';
47
-
48
- const parts: string[] = [];
49
-
50
- if (item.class) parts.push(`class="${item.class}"`);
51
-
52
- if (item.style) {
53
- const css = Object.entries(item.style)
54
- .map(([k, v]) => `${k}: ${v}`)
55
- .join('; ');
56
- parts.push(`style="${css}"`);
57
- }
58
-
59
- if (item.attrs) {
60
- for (const [k, v] of Object.entries(item.attrs)) {
61
- parts.push(v === '' ? k : `${k}="${v}"`);
62
- }
63
- }
64
-
65
- const attrStr = parts.length ? ` ${parts.join(' ')}` : '';
66
- const content = item.children ? item.children.map(renderItem).join('\n') : (item.text ?? '');
67
-
68
- return `<${item.tag}${attrStr}>${content}</${item.tag}>`;
69
- }
70
-
71
- export function loadDocsJson(docsPath: string): DocsJson {
72
- return JSON.parse(readFileSync(docsPath, 'utf-8'));
73
- }
74
-
75
- export function generateHtmlFromDocs(docs: DocsJson): string {
76
- const sections: string[] = [];
77
-
78
- for (const section of docs.sections) {
79
- const examples: string[] = [];
80
-
81
- for (const example of section.examples) {
82
- let html = '';
83
-
84
- if (example.html) {
85
- html = example.html;
86
- } else if (example.items) {
87
- const items = example.items.map(renderItem).join('\n');
88
- html = example.layout === 'row' ? `<div class="ui-row">${items}</div>` : items;
89
- }
90
-
91
- if (html) {
92
- examples.push(`<div class="test-example">${html}</div>`);
93
- }
94
- }
95
-
96
- sections.push(`
97
- <div class="test-section">
98
- <div class="test-section-title">${section.title}</div>
99
- ${examples.join('\n')}
100
- </div>
101
- `);
102
- }
103
-
104
- return `<div class="test-container">${sections.join('')}</div>`;
105
- }
106
-
107
- export async function setupVisualTest(
108
- page: Page,
109
- options: {
110
- html: string;
111
- css?: string;
112
- includeTokens?: boolean;
113
- },
114
- ): Promise<void> {
115
- const { html, css, includeTokens = true } = options;
116
- const componentCss = includeTokens ? loadCss() : css || '';
117
-
118
- const fullHtml = `
119
- <!DOCTYPE html>
120
- <html>
121
- <head>
122
- <meta charset="utf-8">
123
- <style>${scaffoldCss}</style>
124
- <style>${componentCss}</style>
125
- </head>
126
- <body>${html}</body>
127
- </html>
128
- `;
129
-
130
- await page.setContent(fullHtml);
131
- }
132
-
133
- export async function setupVisualTestFromApi(page: Page, apiPath: string): Promise<void> {
134
- const api = loadComponentApi(apiPath);
135
- const html = generateVariationsHtml(api);
136
- await setupVisualTest(page, { html });
137
- }
138
-
139
- export async function setupVisualTestFromDocs(page: Page, docsPath: string): Promise<void> {
140
- const docs = loadDocsJson(docsPath);
141
- const html = generateHtmlFromDocs(docs);
142
- await setupVisualTest(page, { html });
143
- }
144
-
145
- export async function saveForLostPixel(page: Page, name: string): Promise<void> {
146
- mkdirSync(LOSTPIXEL_DIR, { recursive: true });
147
- const screenshot = await page.screenshot({ fullPage: true });
148
- writeFileSync(resolve(LOSTPIXEL_DIR, `${name}.png`), screenshot);
149
- }