@donkit-ai/design-system 0.2.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.
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@donkit-ai/design-system",
3
+ "version": "0.2.3",
4
+ "description": "Donkit Design System - minimal design tokens and React components",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js",
9
+ "./tokens.css": "./src/styles/tokens.css",
10
+ "./components/*": "./src/components/*.jsx"
11
+ },
12
+ "files": [
13
+ "src/components",
14
+ "src/styles",
15
+ "src/index.js",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "dev": "vite",
20
+ "build": "vite build",
21
+ "preview": "vite preview"
22
+ },
23
+ "peerDependencies": {
24
+ "react": ">=18.0.0 <21.0.0",
25
+ "react-dom": ">=18.0.0 <21.0.0"
26
+ },
27
+ "dependencies": {
28
+ "lucide-react": "^0.445.0"
29
+ },
30
+ "devDependencies": {
31
+ "@vitejs/plugin-react": "^4.3.1",
32
+ "vite": "^5.4.0",
33
+ "react": "^19.0.0",
34
+ "react-dom": "^19.0.0"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://gitlab.com/donkit-ai/platform/design/design-system.git"
39
+ }
40
+ }
@@ -0,0 +1,43 @@
1
+ .ds-accordion {
2
+ border: 1px solid var(--color-border);
3
+ border-radius: var(--radius-s);
4
+ overflow: hidden;
5
+ }
6
+
7
+ .ds-accordion__header {
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: space-between;
11
+ width: 100%;
12
+ padding: var(--space-m);
13
+ background-color: var(--color-item-bg);
14
+ border: none;
15
+ color: var(--color-txt-icon-1);
16
+ font-size: var(--font-size-h4);
17
+ font-family: inherit;
18
+ cursor: pointer;
19
+ transition: background-color var(--transition-normal);
20
+ }
21
+
22
+ .ds-accordion__header:hover {
23
+ background-color: var(--color-item-bg-hover);
24
+ }
25
+
26
+ .ds-accordion__title {
27
+ font-weight: 400;
28
+ }
29
+
30
+ .ds-accordion__icon {
31
+ flex-shrink: 0;
32
+ color: var(--color-txt-icon-2);
33
+ transition: transform var(--transition-normal);
34
+ }
35
+
36
+ .ds-accordion__icon--expanded {
37
+ transform: rotate(180deg);
38
+ }
39
+
40
+ .ds-accordion__content {
41
+ padding: var(--space-m);
42
+ border-top: 1px solid var(--color-border);
43
+ }
@@ -0,0 +1,35 @@
1
+ import React, { useState } from 'react';
2
+ import { ChevronDown } from 'lucide-react';
3
+ import './Accordion.css';
4
+
5
+ export function Accordion({
6
+ children,
7
+ title,
8
+ defaultExpanded = false,
9
+ ...props
10
+ }) {
11
+ const [isExpanded, setIsExpanded] = useState(defaultExpanded);
12
+
13
+ return (
14
+ <div className="ds-accordion" {...props}>
15
+ <button
16
+ type="button"
17
+ className="ds-accordion__header"
18
+ onClick={() => setIsExpanded(!isExpanded)}
19
+ aria-expanded={isExpanded}
20
+ >
21
+ <span className="ds-accordion__title">{title}</span>
22
+ <ChevronDown
23
+ size={20}
24
+ strokeWidth={1.5}
25
+ className={`ds-accordion__icon ${isExpanded ? 'ds-accordion__icon--expanded' : ''}`}
26
+ />
27
+ </button>
28
+ {isExpanded && (
29
+ <div className="ds-accordion__content">
30
+ {children}
31
+ </div>
32
+ )}
33
+ </div>
34
+ );
35
+ }
@@ -0,0 +1,98 @@
1
+ .ds-alert {
2
+ display: flex;
3
+ align-items: flex-start;
4
+ gap: var(--space-s);
5
+ padding: var(--space-s);
6
+ border-radius: var(--radius-s);
7
+ border: 1px solid;
8
+ }
9
+
10
+ .ds-alert--no-title {
11
+ align-items: center;
12
+ }
13
+
14
+ .ds-alert__icon {
15
+ flex-shrink: 0;
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ }
20
+
21
+ .ds-alert:not(.ds-alert--no-title) .ds-alert__icon {
22
+ margin-top: 2px;
23
+ }
24
+
25
+ .ds-alert__content {
26
+ flex: 1;
27
+ min-width: 0;
28
+ }
29
+
30
+ .ds-alert__title {
31
+ font-size: var(--font-size-p1);
32
+ font-weight: 400;
33
+ margin-bottom: var(--space-xs);
34
+ }
35
+
36
+ .ds-alert__message {
37
+ font-size: var(--font-size-p2);
38
+ line-height: 1.5;
39
+ }
40
+
41
+ .ds-alert__close {
42
+ flex-shrink: 0;
43
+ background: none;
44
+ border: none;
45
+ color: inherit;
46
+ cursor: pointer;
47
+ padding: 0;
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: center;
51
+ opacity: 0.7;
52
+ transition: opacity var(--transition-normal);
53
+ }
54
+
55
+ .ds-alert__close:hover {
56
+ opacity: 1;
57
+ }
58
+
59
+ /* Variants */
60
+ .ds-alert--info {
61
+ background-color: var(--color-status-info-bg);
62
+ border-color: var(--color-status-info);
63
+ color: var(--color-status-info);
64
+ }
65
+
66
+ .ds-alert--info .ds-alert__message {
67
+ color: var(--color-txt-icon-1);
68
+ }
69
+
70
+ .ds-alert--success {
71
+ background-color: var(--color-status-success-bg);
72
+ border-color: var(--color-status-success);
73
+ color: var(--color-status-success);
74
+ }
75
+
76
+ .ds-alert--success .ds-alert__message {
77
+ color: var(--color-txt-icon-1);
78
+ }
79
+
80
+ .ds-alert--warning {
81
+ background-color: var(--color-status-warning-bg);
82
+ border-color: var(--color-status-warning);
83
+ color: var(--color-status-warning);
84
+ }
85
+
86
+ .ds-alert--warning .ds-alert__message {
87
+ color: var(--color-txt-icon-1);
88
+ }
89
+
90
+ .ds-alert--error {
91
+ background-color: var(--color-status-error-bg);
92
+ border-color: var(--color-status-error);
93
+ color: var(--color-status-error);
94
+ }
95
+
96
+ .ds-alert--error .ds-alert__message {
97
+ color: var(--color-txt-icon-1);
98
+ }
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { Info, CheckCircle, AlertTriangle, XCircle, X } from 'lucide-react';
3
+ import './Alert.css';
4
+
5
+ const ICON_MAP = {
6
+ info: Info,
7
+ success: CheckCircle,
8
+ warning: AlertTriangle,
9
+ error: XCircle,
10
+ };
11
+
12
+ export function Alert({
13
+ children,
14
+ variant = 'info',
15
+ title,
16
+ onClose,
17
+ role,
18
+ ...props
19
+ }) {
20
+ const Icon = ICON_MAP[variant];
21
+ const alertRole = role || (variant === 'error' ? 'alert' : 'status');
22
+
23
+ return (
24
+ <div className={`ds-alert ds-alert--${variant} ${!title ? 'ds-alert--no-title' : ''}`} role={alertRole} {...props}>
25
+ {Icon && (
26
+ <div className="ds-alert__icon">
27
+ <Icon size={24} strokeWidth={1.5} />
28
+ </div>
29
+ )}
30
+ <div className="ds-alert__content">
31
+ {title && <div className="ds-alert__title">{title}</div>}
32
+ {children && <div className="ds-alert__message">{children}</div>}
33
+ </div>
34
+ {onClose && (
35
+ <button
36
+ type="button"
37
+ className="ds-alert__close"
38
+ onClick={onClose}
39
+ aria-label="Close alert"
40
+ >
41
+ <X size={24} strokeWidth={1.5} />
42
+ </button>
43
+ )}
44
+ </div>
45
+ );
46
+ }
@@ -0,0 +1,57 @@
1
+ .ds-badge {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ font-weight: 400;
5
+ white-space: nowrap;
6
+ }
7
+
8
+ /* Sizes */
9
+ .ds-badge--small {
10
+ padding: 0 4px;
11
+ font-size: var(--font-size-p3);
12
+ letter-spacing: var(--letter-spacing-p3);
13
+ border-radius: var(--radius-xs);
14
+ }
15
+
16
+ .ds-badge--medium {
17
+ padding: 2px var(--space-xs);
18
+ font-size: var(--font-size-p2);
19
+ letter-spacing: var(--letter-spacing-p2);
20
+ border-radius: var(--radius-s);
21
+ }
22
+
23
+ .ds-badge--default {
24
+ background-color: var(--color-item-bg);
25
+ color: var(--color-txt-icon-2);
26
+ border: 1px solid var(--color-txt-icon-2);
27
+ }
28
+
29
+ .ds-badge--info {
30
+ background-color: var(--color-status-info-bg);
31
+ color: var(--color-status-info);
32
+ border: 1px solid var(--color-status-info);
33
+ }
34
+
35
+ .ds-badge--success {
36
+ background-color: var(--color-status-success-bg);
37
+ color: var(--color-status-success);
38
+ border: 1px solid var(--color-status-success);
39
+ }
40
+
41
+ .ds-badge--warning {
42
+ background-color: var(--color-status-warning-bg);
43
+ color: var(--color-status-warning);
44
+ border: 1px solid var(--color-status-warning);
45
+ }
46
+
47
+ .ds-badge--error {
48
+ background-color: var(--color-status-error-bg);
49
+ color: var(--color-status-error);
50
+ border: 1px solid var(--color-status-error);
51
+ }
52
+
53
+ .ds-badge--accent {
54
+ background-color: var(--color-red-10);
55
+ color: var(--color-accent);
56
+ border: 1px solid var(--color-accent);
57
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import './Badge.css';
3
+
4
+ export function Badge({
5
+ children,
6
+ variant = 'default',
7
+ size = 'medium',
8
+ role,
9
+ ...props
10
+ }) {
11
+ const className = [
12
+ 'ds-badge',
13
+ `ds-badge--${variant}`,
14
+ `ds-badge--${size}`,
15
+ ].filter(Boolean).join(' ');
16
+
17
+ // Use role="status" for informational badges (info, success, warning, error)
18
+ const badgeRole = role || (['info', 'success', 'warning', 'error'].includes(variant) ? 'status' : undefined);
19
+
20
+ return (
21
+ <span className={className} role={badgeRole} {...props}>
22
+ {children}
23
+ </span>
24
+ );
25
+ }
@@ -0,0 +1,94 @@
1
+ .ds-button {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ gap: var(--space-xs);
5
+ font-size: var(--font-size-p1);
6
+ font-weight: 400;
7
+ font-family: inherit;
8
+ line-height: 1;
9
+ border: none;
10
+ cursor: pointer;
11
+ transition: background-color var(--transition-normal),
12
+ border-color var(--transition-normal),
13
+ opacity var(--transition-normal);
14
+ white-space: nowrap;
15
+ }
16
+
17
+ .ds-button:disabled {
18
+ opacity: 0.5;
19
+ cursor: not-allowed;
20
+ }
21
+
22
+ .ds-button__icon {
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ }
27
+
28
+ /* Variants */
29
+ .ds-button--primary {
30
+ color: var(--color-white);
31
+ background-color: var(--color-accent);
32
+ }
33
+
34
+ .ds-button--primary:hover:not(:disabled) {
35
+ background-color: var(--color-accent-hover);
36
+ }
37
+
38
+ .ds-button--secondary {
39
+ color: var(--color-txt-icon-1);
40
+ background-color: transparent;
41
+ border: 1px solid var(--color-border);
42
+ }
43
+
44
+ .ds-button--secondary:hover:not(:disabled) {
45
+ background-color: var(--color-item-bg-hover);
46
+ border-color: var(--color-border-hover);
47
+ }
48
+
49
+ .ds-button--ghost {
50
+ color: var(--color-txt-icon-2);
51
+ background-color: transparent;
52
+ }
53
+
54
+ .ds-button--ghost:hover:not(:disabled) {
55
+ background-color: var(--color-item-bg-hover);
56
+ color: var(--color-txt-icon-1);
57
+ }
58
+
59
+ /* Sizes */
60
+ .ds-button--small {
61
+ height: calc(20px + var(--space-xs) * 2);
62
+ padding: 0 var(--space-s);
63
+ font-size: var(--font-size-p2);
64
+ border-radius: var(--radius-xs);
65
+ }
66
+
67
+ .ds-button--medium {
68
+ height: calc(24px + var(--space-s) * 2);
69
+ padding: 0 var(--space-s);
70
+ font-size: var(--font-size-p1);
71
+ border-radius: var(--radius-s);
72
+ }
73
+
74
+ .ds-button--large {
75
+ height: calc(28px + var(--space-m) * 2);
76
+ padding: 0 var(--space-m);
77
+ font-size: var(--font-size-h4);
78
+ border-radius: var(--radius-s);
79
+ gap: var(--space-s);
80
+ }
81
+
82
+ /* Full width */
83
+ .ds-button--full {
84
+ width: 100%;
85
+ justify-content: center;
86
+ }
87
+
88
+ /* Icon only */
89
+ .ds-button--icon-only {
90
+ aspect-ratio: 1;
91
+ justify-content: center;
92
+ padding-left: 0;
93
+ padding-right: 0;
94
+ }
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import './Button.css';
3
+
4
+ export function Button({
5
+ children,
6
+ variant = 'primary',
7
+ size = 'medium',
8
+ fullWidth = false,
9
+ icon,
10
+ disabled = false,
11
+ onClick,
12
+ type = 'button',
13
+ 'aria-label': ariaLabel,
14
+ ...props
15
+ }) {
16
+ const isIconOnly = icon && !children;
17
+
18
+ const className = [
19
+ 'ds-button',
20
+ `ds-button--${variant}`,
21
+ `ds-button--${size}`,
22
+ fullWidth && 'ds-button--full',
23
+ isIconOnly && 'ds-button--icon-only',
24
+ ].filter(Boolean).join(' ');
25
+
26
+ return (
27
+ <button
28
+ type={type}
29
+ className={className}
30
+ disabled={disabled}
31
+ onClick={onClick}
32
+ aria-label={ariaLabel}
33
+ {...props}
34
+ >
35
+ {icon && !isIconOnly && <span className="ds-button__icon" aria-hidden="true">{icon}</span>}
36
+ {children}
37
+ {isIconOnly && <span className="ds-button__icon" aria-hidden="true">{icon}</span>}
38
+ </button>
39
+ );
40
+ }
@@ -0,0 +1,32 @@
1
+ .ds-card {
2
+ background-color: transparent;
3
+ border-radius: var(--radius-s);
4
+ border: 1px solid var(--color-border);
5
+ transition: border-color var(--transition-normal), background-color var(--transition-normal);
6
+ }
7
+
8
+ .ds-card--interactive {
9
+ cursor: pointer;
10
+ background-color: var(--color-item-bg);
11
+ }
12
+
13
+ .ds-card--interactive:hover {
14
+ border-color: var(--color-border-hover);
15
+ background-color: var(--color-item-bg-hover);
16
+ }
17
+
18
+ .ds-card--none {
19
+ padding: 0;
20
+ }
21
+
22
+ .ds-card--small {
23
+ padding: var(--space-s);
24
+ }
25
+
26
+ .ds-card--medium {
27
+ padding: var(--space-m);
28
+ }
29
+
30
+ .ds-card--large {
31
+ padding: var(--space-l);
32
+ }
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import './Card.css';
3
+
4
+ export function Card({
5
+ children,
6
+ padding = 'medium',
7
+ variant = 'info',
8
+ hover = false, // deprecated, use variant="interactive"
9
+ onClick,
10
+ ...props
11
+ }) {
12
+ const cardVariant = hover ? 'interactive' : variant;
13
+ const isInteractive = cardVariant === 'interactive' || onClick;
14
+
15
+ const className = [
16
+ 'ds-card',
17
+ `ds-card--${padding}`,
18
+ isInteractive && 'ds-card--interactive',
19
+ ].filter(Boolean).join(' ');
20
+
21
+ const Element = isInteractive && onClick ? 'button' : 'div';
22
+ const interactiveProps = isInteractive && onClick ? {
23
+ type: 'button',
24
+ onClick,
25
+ } : {};
26
+
27
+ return (
28
+ <Element
29
+ className={className}
30
+ role={isInteractive && !onClick ? 'article' : undefined}
31
+ {...interactiveProps}
32
+ {...props}
33
+ >
34
+ {children}
35
+ </Element>
36
+ );
37
+ }
@@ -0,0 +1,30 @@
1
+ .ds-code-inline {
2
+ font-family: 'Fira Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, monospace;
3
+ font-size: 0.9em;
4
+ background-color: var(--color-code-bg);
5
+ color: var(--color-txt-icon-1);
6
+ padding: 2px 6px;
7
+ border-radius: var(--radius-s);
8
+ white-space: nowrap;
9
+ }
10
+
11
+ .ds-code-block {
12
+ font-family: 'Fira Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, monospace;
13
+ font-size: var(--font-size-p2);
14
+ background-color: var(--color-code-bg);
15
+ color: var(--color-txt-icon-1);
16
+ padding: var(--space-s);
17
+ border-radius: var(--radius-s);
18
+ overflow-x: auto;
19
+ line-height: 1.6;
20
+ margin: 0;
21
+ }
22
+
23
+ .ds-code-block code {
24
+ background: none;
25
+ border: none;
26
+ padding: 0;
27
+ font-family: inherit;
28
+ font-size: inherit;
29
+ color: inherit;
30
+ }
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import { CodeAccordion } from './CodeAccordion';
3
+ import './Code.css';
4
+
5
+ export function Code({ children, block = false, collapsible = false, title = 'Code', defaultExpanded = false, ...props }) {
6
+ if (block) {
7
+ if (collapsible) {
8
+ return (
9
+ <CodeAccordion title={title} defaultExpanded={defaultExpanded} {...props}>
10
+ {children}
11
+ </CodeAccordion>
12
+ );
13
+ }
14
+
15
+ return (
16
+ <pre className="ds-code-block" {...props}>
17
+ <code>{children}</code>
18
+ </pre>
19
+ );
20
+ }
21
+
22
+ return (
23
+ <code className="ds-code-inline" {...props}>
24
+ {children}
25
+ </code>
26
+ );
27
+ }
@@ -0,0 +1,59 @@
1
+ .ds-code-accordion {
2
+ border: 1px solid var(--color-border);
3
+ border-radius: var(--radius-s);
4
+ overflow: hidden;
5
+ }
6
+
7
+ .ds-code-accordion__header {
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: space-between;
11
+ width: 100%;
12
+ padding: var(--space-xs);
13
+ background-color: var(--color-item-bg);
14
+ border: none;
15
+ color: var(--color-txt-icon-1);
16
+ font-size: var(--font-size-p1);
17
+ font-family: inherit;
18
+ cursor: pointer;
19
+ transition: background-color var(--transition-normal);
20
+ }
21
+
22
+ .ds-code-accordion__header:hover {
23
+ background-color: var(--color-item-bg-hover);
24
+ }
25
+
26
+ .ds-code-accordion__title {
27
+ font-weight: 400;
28
+ }
29
+
30
+ .ds-code-accordion__icon {
31
+ flex-shrink: 0;
32
+ color: var(--color-txt-icon-2);
33
+ transition: transform var(--transition-normal);
34
+ }
35
+
36
+ .ds-code-accordion__icon--expanded {
37
+ transform: rotate(180deg);
38
+ }
39
+
40
+ .ds-code-accordion__content {
41
+ font-family: 'Fira Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, monospace;
42
+ font-size: var(--font-size-p2);
43
+ background-color: var(--color-code-bg);
44
+ color: var(--color-txt-icon-1);
45
+ padding: var(--space-xs);
46
+ border-top: 1px solid var(--color-border);
47
+ overflow-x: auto;
48
+ line-height: 1.6;
49
+ margin: 0;
50
+ }
51
+
52
+ .ds-code-accordion__content code {
53
+ background: none;
54
+ border: none;
55
+ padding: 0;
56
+ font-family: inherit;
57
+ font-size: inherit;
58
+ color: inherit;
59
+ }