@gv-tech/design-system 0.8.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 (88) hide show
  1. package/.github/CODEOWNERS +2 -0
  2. package/.github/CONTRIBUTING.md +38 -0
  3. package/.github/FUNDING.yml +4 -0
  4. package/.github/PULL_REQUEST_TEMPLATE/build.md +5 -0
  5. package/.github/PULL_REQUEST_TEMPLATE/standard.md +3 -0
  6. package/.github/RELEASING.md +37 -0
  7. package/.github/copilot-instructions.md +93 -0
  8. package/.github/workflows/ci.yml +82 -0
  9. package/.github/workflows/codeql-analysis.yml +34 -0
  10. package/.github/workflows/release-please.yml +53 -0
  11. package/.husky/pre-commit +1 -0
  12. package/.nvmrc +1 -0
  13. package/.prettierignore +1 -0
  14. package/.storybook/.preview-head.html +1 -0
  15. package/.storybook/main.ts +38 -0
  16. package/.storybook/preview.tsx +30 -0
  17. package/.tool-versions +1 -0
  18. package/.vscode/launch.json +22 -0
  19. package/.vscode/settings.json +30 -0
  20. package/.yarn/releases/yarn-4.12.0.cjs +942 -0
  21. package/.yarnrc.yml +7 -0
  22. package/CHANGELOG.md +490 -0
  23. package/LICENSE +21 -0
  24. package/README.md +116 -0
  25. package/SECURITY.md +9 -0
  26. package/babel.config.js +3 -0
  27. package/dist/favicon.ico +0 -0
  28. package/dist/index.demo.html +40 -0
  29. package/dist/index.js +647 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/index.mjs +1053 -0
  32. package/dist/index.mjs.map +1 -0
  33. package/dist/logo192.png +0 -0
  34. package/dist/logo512.png +0 -0
  35. package/dist/manifest.json +25 -0
  36. package/dist/robots.txt +2 -0
  37. package/dist/vendor-DXgJBoQh.mjs +265 -0
  38. package/dist/vendor-DXgJBoQh.mjs.map +1 -0
  39. package/dist/vendor-nZSsnGb7.js +7 -0
  40. package/dist/vendor-nZSsnGb7.js.map +1 -0
  41. package/docs/MIGRATE_TO_GVTECH_SCOPE.md +74 -0
  42. package/eslint.config.mjs +95 -0
  43. package/netlify.toml +6 -0
  44. package/package.json +130 -0
  45. package/public/favicon.ico +0 -0
  46. package/public/index.demo.html +40 -0
  47. package/public/logo192.png +0 -0
  48. package/public/logo512.png +0 -0
  49. package/public/manifest.json +25 -0
  50. package/public/robots.txt +2 -0
  51. package/scripts/validate.js +56 -0
  52. package/serve.json +4 -0
  53. package/src/Avatar.stories.tsx +67 -0
  54. package/src/Avatar.tsx +174 -0
  55. package/src/Badge.stories.tsx +87 -0
  56. package/src/Badge.tsx +76 -0
  57. package/src/Button.stories.tsx +244 -0
  58. package/src/Button.tsx +384 -0
  59. package/src/Icon.stories.tsx +101 -0
  60. package/src/Icon.tsx +64 -0
  61. package/src/Intro.stories.tsx +20 -0
  62. package/src/Link.stories.tsx +69 -0
  63. package/src/Link.tsx +252 -0
  64. package/src/StoryLinkWrapper.d.ts +1 -0
  65. package/src/StoryLinkWrapper.tsx +33 -0
  66. package/src/__tests__/Avatar.test.tsx +28 -0
  67. package/src/__tests__/Badge.test.tsx +25 -0
  68. package/src/__tests__/Button.test.tsx +38 -0
  69. package/src/__tests__/Icon.test.tsx +26 -0
  70. package/src/__tests__/Link.test.tsx +31 -0
  71. package/src/index.ts +13 -0
  72. package/src/mdx.d.ts +5 -0
  73. package/src/setupTests.ts +1 -0
  74. package/src/shared/animation.d.ts +18 -0
  75. package/src/shared/animation.js +60 -0
  76. package/src/shared/global.d.ts +12 -0
  77. package/src/shared/global.js +120 -0
  78. package/src/shared/icons.d.ts +34 -0
  79. package/src/shared/icons.js +282 -0
  80. package/src/shared/styles.d.ts +86 -0
  81. package/src/shared/styles.js +98 -0
  82. package/src/test-utils/axe.ts +25 -0
  83. package/src/types.ts +316 -0
  84. package/tsconfig.build.json +12 -0
  85. package/tsconfig.json +20 -0
  86. package/tsconfig.node.json +10 -0
  87. package/vite.config.ts +35 -0
  88. package/vitest.config.ts +13 -0
@@ -0,0 +1,98 @@
1
+ import { css } from 'styled-components';
2
+
3
+ // Global style variables
4
+ export const background = {
5
+ app: '#F6F9FC',
6
+ appInverse: '#7A8997',
7
+ positive: '#E1FFD4',
8
+ negative: '#FEDED2',
9
+ warning: '#FFF5CF',
10
+ };
11
+
12
+ export const color = {
13
+ // Palette
14
+ primary: '#FF4785', // coral
15
+ secondary: '#1EA7FD', // ocean
16
+ tertiary: '#DDDDDD',
17
+
18
+ orange: '#FC521F',
19
+ gold: '#FFAE00',
20
+ green: '#66BF3C',
21
+ seafoam: '#37D5D3',
22
+ purple: '#6F2CAC',
23
+ ultraviolet: '#2A0481',
24
+
25
+ // Monochrome
26
+ lightest: '#FFFFFF',
27
+ lighter: '#F8F8F8',
28
+ light: '#F3F3F3',
29
+ mediumlight: '#EEEEEE',
30
+ medium: '#DDDDDD',
31
+ mediumdark: '#999999',
32
+ dark: '#666666',
33
+ darker: '#444444',
34
+ darkest: '#333333',
35
+
36
+ border: 'rgba(0,0,0,.1)',
37
+
38
+ // Status
39
+ positive: '#66BF3C',
40
+ negative: '#FF4400',
41
+ warning: '#E69D00',
42
+ };
43
+
44
+ export const spacing = {
45
+ padding: {
46
+ small: 10,
47
+ medium: 20,
48
+ large: 30,
49
+ },
50
+ borderRadius: {
51
+ small: 5,
52
+ default: 10,
53
+ },
54
+ };
55
+
56
+ export const typography = {
57
+ type: {
58
+ primary: '"Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif',
59
+ code: '"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace',
60
+ },
61
+ weight: {
62
+ regular: '400',
63
+ bold: '700',
64
+ extrabold: '800',
65
+ black: '900',
66
+ },
67
+ size: {
68
+ s1: '12',
69
+ s2: '14',
70
+ s3: '16',
71
+ m1: '20',
72
+ m2: '24',
73
+ m3: '28',
74
+ l1: '32',
75
+ l2: '40',
76
+ l3: '48',
77
+ code: '90',
78
+ },
79
+ };
80
+
81
+ export const breakpoint = 600;
82
+ export const pageMargin = '5.55555';
83
+
84
+ export const pageMargins = css`
85
+ padding: 0 ${spacing.padding.medium}px;
86
+ @media (min-width: ${breakpoint * 1}px) {
87
+ margin: 0 ${pageMargin * 1}%;
88
+ }
89
+ @media (min-width: ${breakpoint * 2}px) {
90
+ margin: 0 ${pageMargin * 2}%;
91
+ }
92
+ @media (min-width: ${breakpoint * 3}px) {
93
+ margin: 0 ${pageMargin * 3}%;
94
+ }
95
+ @media (min-width: ${breakpoint * 4}px) {
96
+ margin: 0 ${pageMargin * 4}%;
97
+ }
98
+ `;
@@ -0,0 +1,25 @@
1
+ import axe from 'axe-core';
2
+
3
+ export async function assertNoA11yViolations(container: HTMLElement | Document) {
4
+ const results = await axe.run(container as unknown as Node);
5
+ if (results.violations && results.violations.length > 0) {
6
+ const messages = results.violations
7
+ .map((v: unknown) => {
8
+ const violation = v as {
9
+ id: string;
10
+ impact?: string | null;
11
+ help: string;
12
+ nodes: Array<{ target: string[]; failureSummary?: string; html?: string }>;
13
+ };
14
+
15
+ const nodes = violation.nodes
16
+ .map((n) => ` - ${n.target.join(', ')}: ${n.failureSummary || n.html || ''}`)
17
+ .join('\n');
18
+ return `${violation.id} (${violation.impact || 'unknown'}): ${violation.help}\n${nodes}`;
19
+ })
20
+ .join('\n\n');
21
+ throw new Error(`Accessibility violations detected:\n\n${messages}`);
22
+ }
23
+ }
24
+
25
+ export default assertNoA11yViolations;
package/src/types.ts ADDED
@@ -0,0 +1,316 @@
1
+ /**
2
+ * Shared TypeScript types and utilities for the GVTech Design System
3
+ */
4
+
5
+ import React from 'react';
6
+
7
+ // =============================================================================
8
+ // COMMON TYPES
9
+ // =============================================================================
10
+
11
+ /**
12
+ * Common HTML element attributes that can be extended by components
13
+ */
14
+ export type HTMLAttributes<T = HTMLElement> = React.HTMLAttributes<T>;
15
+
16
+ /**
17
+ * Common button element attributes
18
+ */
19
+ export type ButtonHTMLAttributes = React.ButtonHTMLAttributes<HTMLButtonElement>;
20
+
21
+ /**
22
+ * Common anchor element attributes
23
+ */
24
+ export type AnchorHTMLAttributes = React.AnchorHTMLAttributes<HTMLAnchorElement>;
25
+
26
+ /**
27
+ * Common input element attributes
28
+ */
29
+ export type InputHTMLAttributes = React.InputHTMLAttributes<HTMLInputElement>;
30
+
31
+ // =============================================================================
32
+ // DESIGN SYSTEM CONSTANTS & TYPES
33
+ // =============================================================================
34
+
35
+ /**
36
+ * Available color values in the design system
37
+ */
38
+ export type ColorValue =
39
+ | 'primary'
40
+ | 'secondary'
41
+ | 'tertiary'
42
+ | 'positive'
43
+ | 'negative'
44
+ | 'warning'
45
+ | 'error'
46
+ | 'neutral';
47
+
48
+ /**
49
+ * Available size variants
50
+ */
51
+ export type SizeVariant = 'small' | 'medium' | 'large' | 'tiny';
52
+
53
+ /**
54
+ * Available appearance variants for buttons and similar components
55
+ */
56
+ export type AppearanceVariant =
57
+ | 'primary'
58
+ | 'secondary'
59
+ | 'tertiary'
60
+ | 'outline'
61
+ | 'primaryOutline'
62
+ | 'secondaryOutline';
63
+
64
+ /**
65
+ * Status variants for badges and status indicators
66
+ */
67
+ export type StatusVariant = 'positive' | 'negative' | 'neutral' | 'error' | 'warning';
68
+
69
+ // =============================================================================
70
+ // BRANDED TYPES
71
+ // =============================================================================
72
+
73
+ /**
74
+ * Branded type for icon names to ensure type safety
75
+ */
76
+ export type IconName = string & { readonly __brand: 'IconName' };
77
+
78
+ /**
79
+ * Branded type for CSS color values
80
+ */
81
+ export type CSSColor = string & { readonly __brand: 'CSSColor' };
82
+
83
+ /**
84
+ * Branded type for pixel values
85
+ */
86
+ export type PixelValue = number & { readonly __brand: 'PixelValue' };
87
+
88
+ // =============================================================================
89
+ // UTILITY TYPES
90
+ // =============================================================================
91
+
92
+ /**
93
+ * Extract the props type from a React component
94
+ */
95
+ export type ComponentProps<T> = T extends React.ComponentType<infer P> ? P : never;
96
+
97
+ /**
98
+ * Make all properties of T optional except for K
99
+ */
100
+ export type RequiredKeys<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
101
+
102
+ /**
103
+ * Make all properties of T nullable
104
+ */
105
+ export type Nullable<T> = { [P in keyof T]: T[P] | null };
106
+
107
+ /**
108
+ * Extract the children prop type
109
+ */
110
+ export type ChildrenType = React.ReactNode;
111
+
112
+ /**
113
+ * Common props that most components should accept
114
+ */
115
+ export interface CommonProps {
116
+ /** Additional CSS classes */
117
+ className?: string;
118
+ /** Inline styles */
119
+ style?: React.CSSProperties;
120
+ /** Child elements */
121
+ children?: ChildrenType;
122
+ }
123
+
124
+ /**
125
+ * Props for components that can be disabled
126
+ */
127
+ export interface DisableableProps {
128
+ /** Whether the component is disabled */
129
+ disabled?: boolean;
130
+ }
131
+
132
+ /**
133
+ * Props for components that can show loading state
134
+ */
135
+ export interface LoadableProps {
136
+ /** Whether the component is in a loading state */
137
+ loading?: boolean;
138
+ /** Backwards-compatible alias used across the codebase */
139
+ isLoading?: boolean;
140
+ /** Text to display while loading */
141
+ loadingText?: string;
142
+ }
143
+
144
+ // =============================================================================
145
+ // COMPONENT-SPECIFIC TYPES
146
+ // =============================================================================
147
+
148
+ /**
149
+ * Props for styled components that need size information
150
+ */
151
+ export interface SizeableProps {
152
+ size?: SizeVariant;
153
+ }
154
+
155
+ /**
156
+ * Props for styled components that need appearance information
157
+ */
158
+ export interface AppearanceProps {
159
+ appearance?: AppearanceVariant;
160
+ }
161
+
162
+ /**
163
+ * Props for styled components that need status information
164
+ */
165
+ export interface StatusProps {
166
+ status?: StatusVariant;
167
+ }
168
+
169
+ /**
170
+ * Props for icon components
171
+ */
172
+ export interface IconProps extends CommonProps, React.SVGProps<SVGSVGElement> {
173
+ /** The icon name/key */
174
+ icon: IconName | string;
175
+ /** Whether to display as a block element */
176
+ block?: boolean;
177
+ /** Whether to display as an inline-only variant */
178
+ inline?: boolean;
179
+ }
180
+
181
+ /**
182
+ * Props for avatar components
183
+ */
184
+ export interface AvatarProps extends CommonProps, HTMLAttributes<HTMLDivElement> {
185
+ /** Whether the avatar is in a loading state */
186
+ loading?: boolean;
187
+ /** The username to display (used for initials or alt text) */
188
+ username?: string;
189
+ /** Image source URL for the avatar */
190
+ src?: string;
191
+ /** Size variant of the avatar */
192
+ size?: SizeVariant;
193
+ }
194
+
195
+ /**
196
+ * Props for badge components
197
+ */
198
+ export interface BadgeProps extends CommonProps, HTMLAttributes<HTMLDivElement> {
199
+ /** Status variant that determines the badge styling */
200
+ status?: StatusVariant;
201
+ }
202
+
203
+ /**
204
+ * Props for button components
205
+ */
206
+ export interface ButtonProps extends CommonProps, ButtonHTMLAttributes, DisableableProps, LoadableProps {
207
+ /** Visual appearance variant */
208
+ appearance?: AppearanceVariant;
209
+ /** Size variant */
210
+ size?: SizeVariant;
211
+ /** Whether the button contains only an icon */
212
+ containsIcon?: boolean;
213
+ /** Whether the button should render as a link */
214
+ isLink?: boolean;
215
+ /** Backwards-compatible aliases used in the codebase */
216
+ isLoading?: boolean;
217
+ isDisabled?: boolean;
218
+ isUnclickable?: boolean;
219
+ /** Custom wrapper component */
220
+ ButtonWrapper?: React.ComponentType<Record<string, unknown> & { children: React.ReactNode }>;
221
+ }
222
+
223
+ /**
224
+ * Props for link components
225
+ */
226
+ export interface LinkProps extends CommonProps, AnchorHTMLAttributes {
227
+ /** Whether the link should render as a button */
228
+ isButton?: boolean;
229
+ /** Whether to show an arrow icon */
230
+ withArrow?: boolean;
231
+ /** Whether to use secondary styling */
232
+ secondary?: boolean;
233
+ /** Whether to use tertiary styling */
234
+ tertiary?: boolean;
235
+ /** Whether to remove default chrome/styling */
236
+ nochrome?: boolean;
237
+ /** Whether to use inverse colors */
238
+ inverse?: boolean;
239
+ /** Whether the link contains only an icon */
240
+ containsIcon?: boolean;
241
+ /** Custom wrapper component */
242
+ LinkWrapper?: React.ComponentType<Record<string, unknown> & { children: React.ReactNode }>;
243
+ }
244
+
245
+ // =============================================================================
246
+ // CONSTANTS WITH PROPER TYPING
247
+ // =============================================================================
248
+
249
+ /**
250
+ * Button appearance constants
251
+ */
252
+ export const BUTTON_APPEARANCES = {
253
+ PRIMARY: 'primary',
254
+ PRIMARY_OUTLINE: 'primaryOutline',
255
+ SECONDARY: 'secondary',
256
+ SECONDARY_OUTLINE: 'secondaryOutline',
257
+ TERTIARY: 'tertiary',
258
+ OUTLINE: 'outline',
259
+ } as const;
260
+
261
+ /**
262
+ * Button size constants
263
+ */
264
+ export const BUTTON_SIZES = {
265
+ SMALL: 'small',
266
+ MEDIUM: 'medium',
267
+ } as const;
268
+
269
+ /**
270
+ * Badge status constants
271
+ */
272
+ export const BADGE_STATUSES = {
273
+ POSITIVE: 'positive',
274
+ NEGATIVE: 'negative',
275
+ NEUTRAL: 'neutral',
276
+ ERROR: 'error',
277
+ WARNING: 'warning',
278
+ } as const;
279
+
280
+ /**
281
+ * Avatar size constants with pixel values
282
+ */
283
+ export const AVATAR_SIZES = {
284
+ large: 40,
285
+ medium: 28,
286
+ small: 20,
287
+ tiny: 16,
288
+ } as const;
289
+
290
+ // =============================================================================
291
+ // TYPE GUARDS
292
+ // =============================================================================
293
+
294
+ /**
295
+ * Type guard to check if a value is a valid size variant
296
+ */
297
+ export function isSizeVariant(value: unknown): value is SizeVariant {
298
+ return typeof value === 'string' && ['small', 'medium', 'large', 'tiny'].includes(value);
299
+ }
300
+
301
+ /**
302
+ * Type guard to check if a value is a valid appearance variant
303
+ */
304
+ export function isAppearanceVariant(value: unknown): value is AppearanceVariant {
305
+ return (
306
+ typeof value === 'string' &&
307
+ ['primary', 'secondary', 'tertiary', 'outline', 'primaryOutline', 'secondaryOutline'].includes(value)
308
+ );
309
+ }
310
+
311
+ /**
312
+ * Type guard to check if a value is a valid status variant
313
+ */
314
+ export function isStatusVariant(value: unknown): value is StatusVariant {
315
+ return typeof value === 'string' && ['positive', 'negative', 'neutral', 'error', 'warning'].includes(value);
316
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": false,
5
+ "declaration": true,
6
+ "emitDeclarationOnly": true,
7
+ "declarationMap": true,
8
+ "outDir": "dist",
9
+ "rootDir": "src"
10
+ },
11
+ "include": ["src"]
12
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "skipLibCheck": true,
7
+ "moduleResolution": "bundler",
8
+ "allowImportingTsExtensions": true,
9
+ "resolveJsonModule": true,
10
+ "isolatedModules": true,
11
+ "noEmit": true,
12
+ "jsx": "react-jsx",
13
+ "strict": true,
14
+ "noUnusedLocals": true,
15
+ "noUnusedParameters": true,
16
+ "noFallthroughCasesInSwitch": true
17
+ },
18
+ "include": ["src", ".storybook"],
19
+ "references": [{ "path": "./tsconfig.node.json" }]
20
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "skipLibCheck": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "allowSyntheticDefaultImports": true
8
+ },
9
+ "include": ["vite.config.ts"]
10
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,35 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import { resolve } from 'path';
4
+
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ build: {
8
+ sourcemap: true,
9
+ lib: {
10
+ entry: resolve(__dirname, 'src/index.ts'),
11
+ name: 'GvtechDesign',
12
+ fileName: 'index',
13
+ formats: ['es', 'cjs'],
14
+ },
15
+ rollupOptions: {
16
+ external: ['react', 'react-dom', 'styled-components', 'polished', 'prop-types'],
17
+ output: {
18
+ globals: {
19
+ react: 'React',
20
+ 'react-dom': 'ReactDOM',
21
+ 'styled-components': 'styled',
22
+ polished: 'polished',
23
+ 'prop-types': 'PropTypes',
24
+ },
25
+ manualChunks(id) {
26
+ if (id.includes('node_modules')) {
27
+ if (id.includes('axe-core')) return 'axe';
28
+ if (id.includes('react-docgen')) return 'docgen';
29
+ return 'vendor';
30
+ }
31
+ },
32
+ },
33
+ },
34
+ },
35
+ });
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: 'jsdom',
6
+ globals: true,
7
+ setupFiles: './src/setupTests.ts',
8
+ coverage: {
9
+ provider: 'v8',
10
+ reporter: ['text', 'lcov'],
11
+ },
12
+ },
13
+ });