@freightos/freightwind 1.1.0 → 1.1.2
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/cjs/components/alert.js +1 -1
- package/dist/cjs/components/button.js +1 -1
- package/dist/cjs/components/checkbox.js +1 -1
- package/dist/cjs/components/chip.js +1 -1
- package/dist/cjs/components/radio-group.js +1 -1
- package/dist/cjs/components/switch.js +1 -1
- package/dist/esm/components/alert.js +1 -1
- package/dist/esm/components/button.js +1 -1
- package/dist/esm/components/checkbox.js +1 -1
- package/dist/esm/components/chip.js +1 -1
- package/dist/esm/components/radio-group.js +1 -1
- package/dist/esm/components/switch.js +1 -1
- package/guidelines/Guidelines.md +141 -0
- package/guidelines/design-tokens/colors.md +81 -0
- package/guidelines/design-tokens/spacing.md +45 -0
- package/guidelines/design-tokens/typography.md +87 -0
- package/guidelines/overview-components.md +252 -0
- package/guidelines/overview-icons.md +52 -0
- package/package.json +2 -1
- package/tokens.css +22 -0
|
@@ -88,6 +88,6 @@ const Alert = ({ variant = 'info', title, message, closable = false, onClose, ca
|
|
|
88
88
|
setVisible(false);
|
|
89
89
|
onClose?.();
|
|
90
90
|
}
|
|
91
|
-
}, children: (0, jsx_runtime_1.jsx)("div", { style: { overflow: 'hidden' }, children: (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: !closing && ((0, jsx_runtime_1.jsxs)(react_1.motion.div, { role: "alert", exit: { y: -20 }, transition: { duration: 0.15, ease: 'easeIn' }, className: (0, utils_1.cn)('flex w-full gap-fds-sm rounded-fds-md border-l-4 bg-card pr-fds-sm shadow-[0_0_10px_0_rgba(35,37,55,0.12)]', title ? 'items-start pl-fds-md py-fds-md' : 'items-center pl-fds-lg py-[5px]', config.borderColor), children: [(0, jsx_runtime_1.jsx)(Icon, { size: title ? 20 : 18, className: (0, utils_1.cn)('shrink-0', title && 'mt-[3px]', config.iconColor) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [title && (0, jsx_runtime_1.jsx)("div", { className: "text-fds-h5 font-fds-bold leading-fds-title text-foreground", children: title }), message && ((0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)('text-fds-base text-fds-gray-80 dark:text-white', title && 'mt-fds-xs'), children: message }))] }), showCta && (0, jsx_runtime_1.jsx)("div", { className: "shrink-0", children: callToAction }), showClose && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handleClose, className: "shrink-0 cursor-pointer text-fds-gray-90 hover:text-fds-gray-100 transition-colors", "aria-label": "Close alert", children: (0, jsx_runtime_1.jsx)(icons_1.IconClose, { size: 16 }) }))] })) }) }) }));
|
|
91
|
+
}, children: (0, jsx_runtime_1.jsx)("div", { style: { overflow: 'hidden' }, children: (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: !closing && ((0, jsx_runtime_1.jsxs)(react_1.motion.div, { role: "alert", exit: { y: -20 }, transition: { duration: 0.15, ease: 'easeIn' }, className: (0, utils_1.cn)('flex w-full gap-fds-sm rounded-fds-md border-l-4 bg-card pr-fds-sm shadow-[0_0_10px_0_rgba(35,37,55,0.12)]', title ? 'items-start pl-fds-md py-fds-md' : 'items-center pl-fds-lg py-[5px]', config.borderColor), children: [(0, jsx_runtime_1.jsx)(Icon, { size: title ? 20 : 18, className: (0, utils_1.cn)('shrink-0', title && 'mt-[3px]', config.iconColor) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [title && (0, jsx_runtime_1.jsx)("div", { className: "text-fds-h5 font-fds-bold leading-fds-title text-foreground", children: title }), message && ((0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)('text-fds-base text-fds-gray-80 dark:text-white', title && 'mt-fds-xs'), children: message }))] }), showCta && (0, jsx_runtime_1.jsx)("div", { className: "shrink-0", children: callToAction }), showClose && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handleClose, className: "fw-base shrink-0 cursor-pointer text-fds-gray-90 hover:text-fds-gray-100 transition-colors", "aria-label": "Close alert", children: (0, jsx_runtime_1.jsx)(icons_1.IconClose, { size: 16 }) }))] })) }) }) }));
|
|
92
92
|
};
|
|
93
93
|
exports.Alert = Alert;
|
|
@@ -41,7 +41,7 @@ const React = __importStar(require("react"));
|
|
|
41
41
|
const icon_utils_1 = require("../lib/icon-utils");
|
|
42
42
|
const utils_1 = require("../lib/utils");
|
|
43
43
|
const tooltip_1 = require("./tooltip");
|
|
44
|
-
const buttonVariants = (0, class_variance_authority_1.cva)('inline-flex w-fit items-center justify-center gap-fds-sm whitespace-nowrap rounded-fds-md cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0', {
|
|
44
|
+
const buttonVariants = (0, class_variance_authority_1.cva)('fw-base inline-flex w-fit items-center justify-center gap-fds-sm whitespace-nowrap rounded-fds-md cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0', {
|
|
45
45
|
variants: {
|
|
46
46
|
variant: {
|
|
47
47
|
default: 'bg-fds-blue-30 text-fds-white hover:bg-fds-blue-40',
|
|
@@ -49,7 +49,7 @@ const Checkbox = React.forwardRef(({ checked, onCheckedChange, disabled, error,
|
|
|
49
49
|
setInternalChecked(val);
|
|
50
50
|
onCheckedChange?.(val);
|
|
51
51
|
};
|
|
52
|
-
const box = ((0, jsx_runtime_1.jsx)(CheckboxPrimitive.Root, { ref: ref, id: checkboxId, checked: resolvedChecked, onCheckedChange: handleCheckedChange, disabled: disabled, "aria-invalid": error || undefined, className: (0, utils_1.cn)('peer size-4 shrink-0 rounded-fds-sm border cursor-pointer transition-colors', 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring', disabled
|
|
52
|
+
const box = ((0, jsx_runtime_1.jsx)(CheckboxPrimitive.Root, { ref: ref, id: checkboxId, checked: resolvedChecked, onCheckedChange: handleCheckedChange, disabled: disabled, "aria-invalid": error || undefined, className: (0, utils_1.cn)('fw-base peer size-4 shrink-0 rounded-fds-sm border cursor-pointer transition-colors', 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring', disabled
|
|
53
53
|
? 'cursor-not-allowed bg-fds-gray-10 border-fds-gray-20'
|
|
54
54
|
: error
|
|
55
55
|
? 'border-fds-red-30 bg-fds-red-10 data-[state=checked]:bg-fds-red-30'
|
|
@@ -80,5 +80,5 @@ function Chip({ variant = 'default', icon, closable, onClose, badge, children, }
|
|
|
80
80
|
};
|
|
81
81
|
const IconComp = icon ? icon_utils_1.iconMap[icon] : null;
|
|
82
82
|
const iconOnly = !!icon && !children;
|
|
83
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)(chipVariants({ variant }), iconOnly && 'justify-center', badge !== undefined && (typeof badge === 'string' ? badge.length > 0 : badge > 0) && '!pl-0.5'), children: [badge !== undefined && (typeof badge === 'string' ? badge.length > 0 : badge > 0) && ((0, jsx_runtime_1.jsx)("span", { className: (0, utils_1.cn)('mr-1 inline-flex items-center justify-center rounded-full min-w-[16px] h-4 px-1 text-[10px] font-fds-semibold leading-none', badgeColors[variant]), children: typeof badge === 'number' && badge > 99 ? '99+' : badge })), IconComp && (0, jsx_runtime_1.jsx)(IconComp, { size: 12, className: children ? 'mr-fds-sm' : '' }), children, closable && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handleClose, "aria-label": "Remove", className: "ml-fds-sm inline-flex cursor-pointer items-center hover:opacity-70", children: (0, jsx_runtime_1.jsx)(icons_1.IconClose, { size: 10 }) }))] }));
|
|
83
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)(chipVariants({ variant }), iconOnly && 'justify-center', badge !== undefined && (typeof badge === 'string' ? badge.length > 0 : badge > 0) && '!pl-0.5'), children: [badge !== undefined && (typeof badge === 'string' ? badge.length > 0 : badge > 0) && ((0, jsx_runtime_1.jsx)("span", { className: (0, utils_1.cn)('mr-1 inline-flex items-center justify-center rounded-full min-w-[16px] h-4 px-1 text-[10px] font-fds-semibold leading-none', badgeColors[variant]), children: typeof badge === 'number' && badge > 99 ? '99+' : badge })), IconComp && (0, jsx_runtime_1.jsx)(IconComp, { size: 12, className: children ? 'mr-fds-sm' : '' }), children, closable && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: handleClose, "aria-label": "Remove", className: "fw-base ml-fds-sm inline-flex cursor-pointer items-center hover:opacity-70", children: (0, jsx_runtime_1.jsx)(icons_1.IconClose, { size: 10 }) }))] }));
|
|
84
84
|
}
|
|
@@ -45,7 +45,7 @@ const RadioGroup = React.forwardRef(({ className, ...props }, ref) => {
|
|
|
45
45
|
exports.RadioGroup = RadioGroup;
|
|
46
46
|
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
|
47
47
|
const RadioGroupItem = React.forwardRef(({ className, ...props }, ref) => {
|
|
48
|
-
return ((0, jsx_runtime_1.jsx)(RadioGroupPrimitive.Item, { ref: ref, className: (0, utils_1.cn)('h-[16px] w-[16px] cursor-pointer rounded-full border border-fds-gray-30 bg-white ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue-30 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:border-fds-gray-20 disabled:bg-fds-gray-10 data-[state=checked]:border-fds-blue-30 data-[state=checked]:disabled:border-fds-gray-30', className), ...props, children: (0, jsx_runtime_1.jsx)(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: (0, jsx_runtime_1.jsx)("div", { className: "h-[8px] w-[8px] rounded-full bg-fds-blue-30 [[disabled]_&]:bg-fds-gray-30" }) }) }));
|
|
48
|
+
return ((0, jsx_runtime_1.jsx)(RadioGroupPrimitive.Item, { ref: ref, className: (0, utils_1.cn)('fw-base h-[16px] w-[16px] cursor-pointer rounded-full border border-fds-gray-30 bg-white ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue-30 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:border-fds-gray-20 disabled:bg-fds-gray-10 data-[state=checked]:border-fds-blue-30 data-[state=checked]:disabled:border-fds-gray-30', className), ...props, children: (0, jsx_runtime_1.jsx)(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: (0, jsx_runtime_1.jsx)("div", { className: "h-[8px] w-[8px] rounded-full bg-fds-blue-30 [[disabled]_&]:bg-fds-gray-30" }) }) }));
|
|
49
49
|
});
|
|
50
50
|
exports.RadioGroupItem = RadioGroupItem;
|
|
51
51
|
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
|
@@ -41,7 +41,7 @@ const React = __importStar(require("react"));
|
|
|
41
41
|
const class_variance_authority_1 = require("class-variance-authority");
|
|
42
42
|
const utils_1 = require("../lib/utils");
|
|
43
43
|
const use_stable_id_1 = require("../lib/use-stable-id");
|
|
44
|
-
const switchVariants = (0, class_variance_authority_1.cva)('peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue-30 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50', {
|
|
44
|
+
const switchVariants = (0, class_variance_authority_1.cva)('fw-base peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue-30 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50', {
|
|
45
45
|
variants: {
|
|
46
46
|
size: {
|
|
47
47
|
lg: 'h-5 w-9',
|
|
@@ -52,5 +52,5 @@ export const Alert = ({ variant = 'info', title, message, closable = false, onCl
|
|
|
52
52
|
setVisible(false);
|
|
53
53
|
onClose?.();
|
|
54
54
|
}
|
|
55
|
-
}, children: _jsx("div", { style: { overflow: 'hidden' }, children: _jsx(AnimatePresence, { initial: false, children: !closing && (_jsxs(motion.div, { role: "alert", exit: { y: -20 }, transition: { duration: 0.15, ease: 'easeIn' }, className: cn('flex w-full gap-fds-sm rounded-fds-md border-l-4 bg-card pr-fds-sm shadow-[0_0_10px_0_rgba(35,37,55,0.12)]', title ? 'items-start pl-fds-md py-fds-md' : 'items-center pl-fds-lg py-[5px]', config.borderColor), children: [_jsx(Icon, { size: title ? 20 : 18, className: cn('shrink-0', title && 'mt-[3px]', config.iconColor) }), _jsxs("div", { className: "flex-1 min-w-0", children: [title && _jsx("div", { className: "text-fds-h5 font-fds-bold leading-fds-title text-foreground", children: title }), message && (_jsx("div", { className: cn('text-fds-base text-fds-gray-80 dark:text-white', title && 'mt-fds-xs'), children: message }))] }), showCta && _jsx("div", { className: "shrink-0", children: callToAction }), showClose && (_jsx("button", { type: "button", onClick: handleClose, className: "shrink-0 cursor-pointer text-fds-gray-90 hover:text-fds-gray-100 transition-colors", "aria-label": "Close alert", children: _jsx(IconClose, { size: 16 }) }))] })) }) }) }));
|
|
55
|
+
}, children: _jsx("div", { style: { overflow: 'hidden' }, children: _jsx(AnimatePresence, { initial: false, children: !closing && (_jsxs(motion.div, { role: "alert", exit: { y: -20 }, transition: { duration: 0.15, ease: 'easeIn' }, className: cn('flex w-full gap-fds-sm rounded-fds-md border-l-4 bg-card pr-fds-sm shadow-[0_0_10px_0_rgba(35,37,55,0.12)]', title ? 'items-start pl-fds-md py-fds-md' : 'items-center pl-fds-lg py-[5px]', config.borderColor), children: [_jsx(Icon, { size: title ? 20 : 18, className: cn('shrink-0', title && 'mt-[3px]', config.iconColor) }), _jsxs("div", { className: "flex-1 min-w-0", children: [title && _jsx("div", { className: "text-fds-h5 font-fds-bold leading-fds-title text-foreground", children: title }), message && (_jsx("div", { className: cn('text-fds-base text-fds-gray-80 dark:text-white', title && 'mt-fds-xs'), children: message }))] }), showCta && _jsx("div", { className: "shrink-0", children: callToAction }), showClose && (_jsx("button", { type: "button", onClick: handleClose, className: "fw-base shrink-0 cursor-pointer text-fds-gray-90 hover:text-fds-gray-100 transition-colors", "aria-label": "Close alert", children: _jsx(IconClose, { size: 16 }) }))] })) }) }) }));
|
|
56
56
|
};
|
|
@@ -5,7 +5,7 @@ import * as React from 'react';
|
|
|
5
5
|
import { iconMap } from '../lib/icon-utils';
|
|
6
6
|
import { cn } from '../lib/utils';
|
|
7
7
|
import { Tooltip } from './tooltip';
|
|
8
|
-
const buttonVariants = cva('inline-flex w-fit items-center justify-center gap-fds-sm whitespace-nowrap rounded-fds-md cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0', {
|
|
8
|
+
const buttonVariants = cva('fw-base inline-flex w-fit items-center justify-center gap-fds-sm whitespace-nowrap rounded-fds-md cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0', {
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
11
11
|
default: 'bg-fds-blue-30 text-fds-white hover:bg-fds-blue-40',
|
|
@@ -13,7 +13,7 @@ const Checkbox = React.forwardRef(({ checked, onCheckedChange, disabled, error,
|
|
|
13
13
|
setInternalChecked(val);
|
|
14
14
|
onCheckedChange?.(val);
|
|
15
15
|
};
|
|
16
|
-
const box = (_jsx(CheckboxPrimitive.Root, { ref: ref, id: checkboxId, checked: resolvedChecked, onCheckedChange: handleCheckedChange, disabled: disabled, "aria-invalid": error || undefined, className: cn('peer size-4 shrink-0 rounded-fds-sm border cursor-pointer transition-colors', 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring', disabled
|
|
16
|
+
const box = (_jsx(CheckboxPrimitive.Root, { ref: ref, id: checkboxId, checked: resolvedChecked, onCheckedChange: handleCheckedChange, disabled: disabled, "aria-invalid": error || undefined, className: cn('fw-base peer size-4 shrink-0 rounded-fds-sm border cursor-pointer transition-colors', 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring', disabled
|
|
17
17
|
? 'cursor-not-allowed bg-fds-gray-10 border-fds-gray-20'
|
|
18
18
|
: error
|
|
19
19
|
? 'border-fds-red-30 bg-fds-red-10 data-[state=checked]:bg-fds-red-30'
|
|
@@ -42,6 +42,6 @@ function Chip({ variant = 'default', icon, closable, onClose, badge, children, }
|
|
|
42
42
|
};
|
|
43
43
|
const IconComp = icon ? iconMap[icon] : null;
|
|
44
44
|
const iconOnly = !!icon && !children;
|
|
45
|
-
return (_jsxs("div", { className: cn(chipVariants({ variant }), iconOnly && 'justify-center', badge !== undefined && (typeof badge === 'string' ? badge.length > 0 : badge > 0) && '!pl-0.5'), children: [badge !== undefined && (typeof badge === 'string' ? badge.length > 0 : badge > 0) && (_jsx("span", { className: cn('mr-1 inline-flex items-center justify-center rounded-full min-w-[16px] h-4 px-1 text-[10px] font-fds-semibold leading-none', badgeColors[variant]), children: typeof badge === 'number' && badge > 99 ? '99+' : badge })), IconComp && _jsx(IconComp, { size: 12, className: children ? 'mr-fds-sm' : '' }), children, closable && (_jsx("button", { type: "button", onClick: handleClose, "aria-label": "Remove", className: "ml-fds-sm inline-flex cursor-pointer items-center hover:opacity-70", children: _jsx(IconClose, { size: 10 }) }))] }));
|
|
45
|
+
return (_jsxs("div", { className: cn(chipVariants({ variant }), iconOnly && 'justify-center', badge !== undefined && (typeof badge === 'string' ? badge.length > 0 : badge > 0) && '!pl-0.5'), children: [badge !== undefined && (typeof badge === 'string' ? badge.length > 0 : badge > 0) && (_jsx("span", { className: cn('mr-1 inline-flex items-center justify-center rounded-full min-w-[16px] h-4 px-1 text-[10px] font-fds-semibold leading-none', badgeColors[variant]), children: typeof badge === 'number' && badge > 99 ? '99+' : badge })), IconComp && _jsx(IconComp, { size: 12, className: children ? 'mr-fds-sm' : '' }), children, closable && (_jsx("button", { type: "button", onClick: handleClose, "aria-label": "Remove", className: "fw-base ml-fds-sm inline-flex cursor-pointer items-center hover:opacity-70", children: _jsx(IconClose, { size: 10 }) }))] }));
|
|
46
46
|
}
|
|
47
47
|
export { Chip, chipVariants };
|
|
@@ -8,7 +8,7 @@ const RadioGroup = React.forwardRef(({ className, ...props }, ref) => {
|
|
|
8
8
|
});
|
|
9
9
|
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
|
10
10
|
const RadioGroupItem = React.forwardRef(({ className, ...props }, ref) => {
|
|
11
|
-
return (_jsx(RadioGroupPrimitive.Item, { ref: ref, className: cn('h-[16px] w-[16px] cursor-pointer rounded-full border border-fds-gray-30 bg-white ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue-30 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:border-fds-gray-20 disabled:bg-fds-gray-10 data-[state=checked]:border-fds-blue-30 data-[state=checked]:disabled:border-fds-gray-30', className), ...props, children: _jsx(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: _jsx("div", { className: "h-[8px] w-[8px] rounded-full bg-fds-blue-30 [[disabled]_&]:bg-fds-gray-30" }) }) }));
|
|
11
|
+
return (_jsx(RadioGroupPrimitive.Item, { ref: ref, className: cn('fw-base h-[16px] w-[16px] cursor-pointer rounded-full border border-fds-gray-30 bg-white ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue-30 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:border-fds-gray-20 disabled:bg-fds-gray-10 data-[state=checked]:border-fds-blue-30 data-[state=checked]:disabled:border-fds-gray-30', className), ...props, children: _jsx(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: _jsx("div", { className: "h-[8px] w-[8px] rounded-full bg-fds-blue-30 [[disabled]_&]:bg-fds-gray-30" }) }) }));
|
|
12
12
|
});
|
|
13
13
|
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
|
14
14
|
export { RadioGroup, RadioGroupItem };
|
|
@@ -5,7 +5,7 @@ import * as React from 'react';
|
|
|
5
5
|
import { cva } from 'class-variance-authority';
|
|
6
6
|
import { cn } from '../lib/utils';
|
|
7
7
|
import { useStableId } from '../lib/use-stable-id';
|
|
8
|
-
const switchVariants = cva('peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue-30 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50', {
|
|
8
|
+
const switchVariants = cva('fw-base peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fds-blue-30 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50', {
|
|
9
9
|
variants: {
|
|
10
10
|
size: {
|
|
11
11
|
lg: 'h-5 w-9',
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# FreightWind Design System
|
|
2
|
+
|
|
3
|
+
FreightWind is the Freightos Design System — a React component library styled with Tailwind CSS v4 and FDS (Freightos Design System) tokens.
|
|
4
|
+
|
|
5
|
+
## Complete Setup
|
|
6
|
+
|
|
7
|
+
Follow ALL steps below to properly set up FreightWind in a React project.
|
|
8
|
+
|
|
9
|
+
### Step 1 — Install dependencies
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @freightos/freightwind @freightos/icons
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`@freightos/icons` is a required peer dependency that provides all iconography.
|
|
16
|
+
|
|
17
|
+
### Step 2 — Load the Open Sans font
|
|
18
|
+
|
|
19
|
+
FreightWind uses **Open Sans** as its font family. Load it from Google Fonts.
|
|
20
|
+
|
|
21
|
+
**Option A — In your HTML `<head>`:**
|
|
22
|
+
|
|
23
|
+
```html
|
|
24
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
25
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
26
|
+
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap" rel="stylesheet" />
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Option B — In Next.js with `next/font`:**
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { Open_Sans } from 'next/font/google';
|
|
33
|
+
|
|
34
|
+
const openSans = Open_Sans({
|
|
35
|
+
subsets: ['latin'],
|
|
36
|
+
weight: ['400', '600', '700'],
|
|
37
|
+
variable: '--font-open-sans',
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Apply to <html> or <body>:
|
|
41
|
+
<html className={openSans.variable}>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then in your CSS, override the font-sans variable:
|
|
45
|
+
|
|
46
|
+
```css
|
|
47
|
+
@theme inline {
|
|
48
|
+
--font-sans: var(--font-open-sans), 'Open Sans', ui-sans-serif, system-ui, sans-serif;
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Step 3 — Import tokens CSS
|
|
53
|
+
|
|
54
|
+
Import the FreightWind tokens stylesheet in your global CSS file. This provides all design tokens, color variables, and custom utilities:
|
|
55
|
+
|
|
56
|
+
```css
|
|
57
|
+
@import "@freightos/freightwind/tokens.css";
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Step 4 — Set up global body styles
|
|
61
|
+
|
|
62
|
+
Add these base styles in your global CSS. These are REQUIRED for components to render correctly:
|
|
63
|
+
|
|
64
|
+
```css
|
|
65
|
+
* {
|
|
66
|
+
-webkit-font-smoothing: antialiased;
|
|
67
|
+
-moz-osx-font-smoothing: grayscale;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
body {
|
|
71
|
+
background-color: var(--ground);
|
|
72
|
+
color: var(--foreground);
|
|
73
|
+
font-family: 'Open Sans', ui-sans-serif, system-ui, -apple-system, sans-serif;
|
|
74
|
+
font-size: 0.875rem; /* 14px — FDS base font size */
|
|
75
|
+
line-height: 1.5;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The `--ground` color is `#f6f6f6` (light gray page canvas). The `--foreground` color is `#3b3f4b` (dark gray text).
|
|
80
|
+
|
|
81
|
+
Font smoothing (`antialiased`) MUST be applied globally — without it, text will look inconsistent across browsers.
|
|
82
|
+
|
|
83
|
+
### Step 5 — Import and use components
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { Button, Alert, Checkbox } from '@freightos/freightwind';
|
|
87
|
+
import { IconSearch } from '@freightos/icons';
|
|
88
|
+
|
|
89
|
+
<Button variant="default" size="md">Submit</Button>
|
|
90
|
+
<Alert variant="info" message="Shipment updated." />
|
|
91
|
+
<Checkbox>Accept terms</Checkbox>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Step 6 — Toast notifications (optional)
|
|
95
|
+
|
|
96
|
+
If using the `message` toast API, wrap your app with `MessageProvider`:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import { MessageProvider, message } from '@freightos/freightwind';
|
|
100
|
+
|
|
101
|
+
// In your root layout:
|
|
102
|
+
<MessageProvider />
|
|
103
|
+
|
|
104
|
+
// Anywhere in your app:
|
|
105
|
+
message.success('Saved successfully');
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Tailwind CSS requirement
|
|
109
|
+
|
|
110
|
+
FreightWind components use Tailwind CSS v4 utility classes. Your project MUST have Tailwind CSS v4 configured. The `tokens.css` file provides all necessary theme tokens and custom utilities needed by the components.
|
|
111
|
+
|
|
112
|
+
If your project uses Tailwind without preflight (to avoid resetting existing components), FreightWind still works — components carry their own element resets via the `fw-base` utility class.
|
|
113
|
+
|
|
114
|
+
## Dark mode
|
|
115
|
+
|
|
116
|
+
FreightWind supports dark mode via a `.dark` class on a parent element (typically `<html>`). The tokens.css includes dark mode overrides for all semantic colors. To enable dark mode, add `class="dark"` to the `<html>` element.
|
|
117
|
+
|
|
118
|
+
## Available components
|
|
119
|
+
|
|
120
|
+
See `overview-components.md` for a full list of components with their props and usage.
|
|
121
|
+
|
|
122
|
+
## Available icons
|
|
123
|
+
|
|
124
|
+
See `overview-icons.md` for details on the icon system.
|
|
125
|
+
|
|
126
|
+
## Design tokens
|
|
127
|
+
|
|
128
|
+
FreightWind uses a custom token system instead of Tailwind defaults:
|
|
129
|
+
|
|
130
|
+
- **Colors**: `design-tokens/colors.md` — semantic color palette (blue, red, green, yellow, gray, purple)
|
|
131
|
+
- **Typography**: `design-tokens/typography.md` — font family, sizes, weights, and line heights
|
|
132
|
+
- **Spacing**: `design-tokens/spacing.md` — consistent spacing scale
|
|
133
|
+
|
|
134
|
+
## Key conventions
|
|
135
|
+
|
|
136
|
+
- Always use FDS tokens (`text-fds-*`, `p-fds-*`, `rounded-fds-*`, `bg-fds-*`) instead of Tailwind defaults
|
|
137
|
+
- All components support both controlled and uncontrolled usage patterns
|
|
138
|
+
- Components with `icon` props accept kebab-case icon names: `"search"`, `"close"`, `"user"`, `"risk"`, etc.
|
|
139
|
+
- Use `cn()` utility (exported from the package) for className merging
|
|
140
|
+
- The page canvas background should be `bg-ground` (#f6f6f6), not white
|
|
141
|
+
- Cards and content areas use `bg-card` (#ffffff) against the ground
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Color Tokens
|
|
2
|
+
|
|
3
|
+
FreightWind uses the FDS (Freightos Design System) color palette. Always use FDS color tokens instead of Tailwind defaults.
|
|
4
|
+
|
|
5
|
+
## Primary — Blue
|
|
6
|
+
|
|
7
|
+
The primary action color. Use for buttons, links, and interactive elements.
|
|
8
|
+
|
|
9
|
+
| Token | Class | Usage |
|
|
10
|
+
|-------|-------|-------|
|
|
11
|
+
| Blue 05 | `bg-fds-blue-05` | Disabled checked state |
|
|
12
|
+
| Blue 10 | `bg-fds-blue-10` | Hover backgrounds, selected states |
|
|
13
|
+
| Blue 20 | `bg-fds-blue-20` | Light accent backgrounds |
|
|
14
|
+
| Blue 30 | `bg-fds-blue-30`, `text-fds-blue-30` | Primary action color (default) |
|
|
15
|
+
| Blue 40 | `bg-fds-blue-40`, `text-fds-blue-40` | Hover state for primary actions |
|
|
16
|
+
| Blue 50 | `bg-fds-blue-50` | Active/pressed state |
|
|
17
|
+
|
|
18
|
+
## Semantic — Red (Error/Danger)
|
|
19
|
+
|
|
20
|
+
| Token | Class | Usage |
|
|
21
|
+
|-------|-------|-------|
|
|
22
|
+
| Red 10 | `bg-fds-red-10` | Error background tint |
|
|
23
|
+
| Red 20 | `bg-fds-red-20` | Error chip background |
|
|
24
|
+
| Red 30 | `bg-fds-red-30`, `text-fds-red-30` | Error/danger primary |
|
|
25
|
+
| Red 40 | `bg-fds-red-40` | Error hover state |
|
|
26
|
+
|
|
27
|
+
## Semantic — Green (Success)
|
|
28
|
+
|
|
29
|
+
| Token | Class | Usage |
|
|
30
|
+
|-------|-------|-------|
|
|
31
|
+
| Green 20 | `bg-fds-green-20` | Success background tint |
|
|
32
|
+
| Green 30 | `bg-fds-green-30`, `text-fds-green-30` | Success primary |
|
|
33
|
+
| Green 40 | `bg-fds-green-40` | Success strong |
|
|
34
|
+
|
|
35
|
+
## Semantic — Yellow (Warning)
|
|
36
|
+
|
|
37
|
+
| Token | Class | Usage |
|
|
38
|
+
|-------|-------|-------|
|
|
39
|
+
| Yellow 20 | `bg-fds-yellow-20` | Warning background tint |
|
|
40
|
+
| Yellow 30 | `bg-fds-yellow-30`, `text-fds-yellow-30` | Warning primary |
|
|
41
|
+
| Yellow 50 | `bg-fds-yellow-50` | Warning strong |
|
|
42
|
+
|
|
43
|
+
## Gray Scale
|
|
44
|
+
|
|
45
|
+
Use for text, borders, backgrounds, and disabled states.
|
|
46
|
+
|
|
47
|
+
| Token | Class | Usage |
|
|
48
|
+
|-------|-------|-------|
|
|
49
|
+
| Gray 05 | `bg-fds-gray-05` | Lightest background |
|
|
50
|
+
| Gray 10 | `bg-fds-gray-10` | Disabled background |
|
|
51
|
+
| Gray 20 | `bg-fds-gray-20` | Disabled borders, light backgrounds |
|
|
52
|
+
| Gray 30 | `bg-fds-gray-30` | Default borders |
|
|
53
|
+
| Gray 40 | `bg-fds-gray-40` | Muted icons |
|
|
54
|
+
| Gray 60 | `bg-fds-gray-60`, `text-fds-gray-60` | Disabled text, secondary text |
|
|
55
|
+
| Gray 80 | `text-fds-gray-80` | Body text |
|
|
56
|
+
| Gray 90 | `text-fds-gray-90` | Dark text |
|
|
57
|
+
| Gray 100 | `text-fds-gray-100`, `bg-fds-gray-100` | Darkest text, tooltip bg |
|
|
58
|
+
|
|
59
|
+
## Accent — Purple
|
|
60
|
+
|
|
61
|
+
| Token | Class | Usage |
|
|
62
|
+
|-------|-------|-------|
|
|
63
|
+
| Purple 1 | `text-fds-purple-1` | Accent text |
|
|
64
|
+
| Purple 2 | `bg-fds-purple-2` | Accent background |
|
|
65
|
+
|
|
66
|
+
## White
|
|
67
|
+
|
|
68
|
+
| Token | Class | Usage |
|
|
69
|
+
|-------|-------|-------|
|
|
70
|
+
| White | `text-fds-white`, `bg-fds-white` | White text/backgrounds |
|
|
71
|
+
|
|
72
|
+
## Decision guide
|
|
73
|
+
|
|
74
|
+
- **Primary actions** (buttons, links): `fds-blue-30`
|
|
75
|
+
- **Destructive actions**: `fds-red-30`
|
|
76
|
+
- **Success states**: `fds-green-30`
|
|
77
|
+
- **Warning states**: `fds-yellow-30`
|
|
78
|
+
- **Body text**: `fds-gray-80`
|
|
79
|
+
- **Disabled text**: `fds-gray-60`
|
|
80
|
+
- **Borders**: `fds-gray-30` (default), `fds-gray-20` (disabled)
|
|
81
|
+
- **Page background**: `bg-ground`
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Spacing Tokens
|
|
2
|
+
|
|
3
|
+
FreightWind uses FDS spacing tokens for consistent sizing. Use these instead of Tailwind defaults.
|
|
4
|
+
|
|
5
|
+
## Spacing scale
|
|
6
|
+
|
|
7
|
+
| Token | Class prefix | Size | Usage |
|
|
8
|
+
|-------|-------------|------|-------|
|
|
9
|
+
| XS | `p-fds-xs`, `m-fds-xs`, `gap-fds-xs` | 4px | Tight spacing (icon gaps, small padding) |
|
|
10
|
+
| SM | `p-fds-sm`, `m-fds-sm`, `gap-fds-sm` | 8px | Default component internal spacing |
|
|
11
|
+
| MD | `p-fds-md`, `m-fds-md`, `gap-fds-md` | 12px | Medium padding, form field spacing |
|
|
12
|
+
| LG | `p-fds-lg`, `m-fds-lg`, `gap-fds-lg` | 16px | Section padding, card padding |
|
|
13
|
+
| XL | `p-fds-xl`, `m-fds-xl`, `gap-fds-xl` | 24px | Large section spacing |
|
|
14
|
+
| XXL | `p-fds-xxl`, `m-fds-xxl`, `gap-fds-xxl` | 32px | Page-level spacing |
|
|
15
|
+
|
|
16
|
+
All spacing tokens work with Tailwind's full set of utility prefixes: `p-`, `px-`, `py-`, `pt-`, `pr-`, `pb-`, `pl-`, `m-`, `mx-`, `my-`, `mt-`, `mr-`, `mb-`, `ml-`, `gap-`, `gap-x-`, `gap-y-`.
|
|
17
|
+
|
|
18
|
+
## Border radius
|
|
19
|
+
|
|
20
|
+
| Token | Class | Value | Usage |
|
|
21
|
+
|-------|-------|-------|-------|
|
|
22
|
+
| SM | `rounded-fds-sm` | 2px | Checkboxes, small elements |
|
|
23
|
+
| MD | `rounded-fds-md` | 4px | Buttons, inputs, cards |
|
|
24
|
+
| LG | `rounded-fds-lg` | 8px | Modals, popovers |
|
|
25
|
+
|
|
26
|
+
## Shadows
|
|
27
|
+
|
|
28
|
+
| Token | Class | Usage |
|
|
29
|
+
|-------|-------|-------|
|
|
30
|
+
| SM | `shadow-fds-sm` | Subtle elevation (dropdowns) |
|
|
31
|
+
| MD | `shadow-fds-md` | Medium elevation (cards, popovers) |
|
|
32
|
+
| LG | `shadow-fds-lg` | High elevation (modals) |
|
|
33
|
+
|
|
34
|
+
## Usage example
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
<div className="p-fds-lg rounded-fds-md shadow-fds-sm">
|
|
38
|
+
<h2 className="mb-fds-sm text-fds-h5 font-fds-bold">Card Title</h2>
|
|
39
|
+
<p className="text-fds-base text-fds-gray-80">Card content with consistent spacing.</p>
|
|
40
|
+
<div className="mt-fds-md flex gap-fds-sm">
|
|
41
|
+
<Button variant="default">Confirm</Button>
|
|
42
|
+
<Button variant="secondary">Cancel</Button>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Typography Tokens
|
|
2
|
+
|
|
3
|
+
FreightWind uses FDS typography tokens. Always use these instead of Tailwind default sizes.
|
|
4
|
+
|
|
5
|
+
## Font family
|
|
6
|
+
|
|
7
|
+
FreightWind uses **Open Sans** (Google Fonts). The font must be loaded externally and applied to the body:
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
body {
|
|
11
|
+
font-family: 'Open Sans', ui-sans-serif, system-ui, -apple-system, sans-serif;
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The tokens.css defines `--font-sans: 'Open Sans', ui-sans-serif, system-ui, sans-serif` which maps to Tailwind's `font-sans` utility. You can use `className="font-sans"` to apply it, but it's better to set it on the body element directly.
|
|
16
|
+
|
|
17
|
+
Required font weights: **400** (regular), **600** (semibold), **700** (bold).
|
|
18
|
+
|
|
19
|
+
## Font smoothing
|
|
20
|
+
|
|
21
|
+
All text MUST have antialiased font smoothing. Apply globally:
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
* {
|
|
25
|
+
-webkit-font-smoothing: antialiased;
|
|
26
|
+
-moz-osx-font-smoothing: grayscale;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Without this, text rendering will look inconsistent across browsers, especially on macOS.
|
|
31
|
+
|
|
32
|
+
## Base font size
|
|
33
|
+
|
|
34
|
+
The default body font size is **14px** (`0.875rem`), not the browser default 16px:
|
|
35
|
+
|
|
36
|
+
```css
|
|
37
|
+
body {
|
|
38
|
+
font-size: 0.875rem;
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Font sizes
|
|
43
|
+
|
|
44
|
+
| Token | Class | Size | Usage |
|
|
45
|
+
|-------|-------|------|-------|
|
|
46
|
+
| XS | `text-fds-xs` | 10px | Captions, fine print |
|
|
47
|
+
| SM | `text-fds-sm` | 12px | Small labels, badges, chips |
|
|
48
|
+
| Base | `text-fds-base` | 14px | Body text (default) |
|
|
49
|
+
| H6 | `text-fds-h6` | 16px | Small headings, large labels |
|
|
50
|
+
| H5 | `text-fds-h5` | 18px | Section headings |
|
|
51
|
+
| H4 | `text-fds-h4` | 22px | Page sub-headings |
|
|
52
|
+
| H3 | `text-fds-h3` | 24px | Page headings |
|
|
53
|
+
| H2 | `text-fds-h2` | 32px | Major headings |
|
|
54
|
+
| H1 | `text-fds-h1` | 40px | Hero headings |
|
|
55
|
+
|
|
56
|
+
## Font weights
|
|
57
|
+
|
|
58
|
+
| Token | Class | Weight | Usage |
|
|
59
|
+
|-------|-------|--------|-------|
|
|
60
|
+
| Regular | `font-fds-regular` | 400 | Body text |
|
|
61
|
+
| Semibold | `font-fds-semibold` | 600 | Labels, emphasis |
|
|
62
|
+
| Bold | `font-fds-bold` | 700 | Headings, strong emphasis |
|
|
63
|
+
|
|
64
|
+
## Line heights
|
|
65
|
+
|
|
66
|
+
| Token | Class | Value | Usage |
|
|
67
|
+
|-------|-------|-------|-------|
|
|
68
|
+
| Title | `leading-fds-title` | 1.25 | Headings (h1-h6) |
|
|
69
|
+
| Body | `leading-fds-body` | 1.5 | Body text, paragraphs |
|
|
70
|
+
|
|
71
|
+
## Usage patterns
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
// Page heading
|
|
75
|
+
<h1 className="text-fds-h3 font-fds-bold leading-fds-title text-foreground">Page Title</h1>
|
|
76
|
+
|
|
77
|
+
// Body text
|
|
78
|
+
<p className="text-fds-base text-fds-gray-80">Body content here.</p>
|
|
79
|
+
|
|
80
|
+
// Small label
|
|
81
|
+
<span className="text-fds-sm font-fds-semibold text-fds-gray-60">Label</span>
|
|
82
|
+
|
|
83
|
+
// Caption
|
|
84
|
+
<span className="text-fds-xs text-fds-gray-60">Fine print</span>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Heading size classes (`text-fds-h1` through `text-fds-h6`) set font-size only. Always pair with `leading-fds-title` for proper line height.
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# Components Overview
|
|
2
|
+
|
|
3
|
+
## Component list
|
|
4
|
+
|
|
5
|
+
| Component | Purpose | Key props |
|
|
6
|
+
|-----------|---------|-----------|
|
|
7
|
+
| `Button` | Primary action trigger | `variant`, `size`, `icon`, `loading`, `tooltip`, `fullWidth` |
|
|
8
|
+
| `Alert` | Contextual feedback banner | `variant`, `title`, `message`, `closable`, `callToAction` |
|
|
9
|
+
| `Avatar` | User or entity representation | `variant`, `size`, `icon`, `title`, `flag`, `src` |
|
|
10
|
+
| `Badge` | Numeric indicator or status dot | `value`, `size`, `variant`, `status`, `text` |
|
|
11
|
+
| `Checkbox` | Boolean or indeterminate toggle | `checked`, `onCheckedChange`, `disabled`, `error`, `children` |
|
|
12
|
+
| `Chip` | Compact label with optional badge | `variant`, `icon`, `closable`, `badge`, `children` |
|
|
13
|
+
| `MessageProvider` / `message` | Toast notifications | Provider wraps app; `message.success()`, `.info()`, `.warning()`, `.error()` |
|
|
14
|
+
| `PopConfirm` | Confirmation popover | `title`, `onConfirm`, `onCancel`, `placement`, `slotProps` |
|
|
15
|
+
| `RadioButtonGroup` | Segmented button selection | `options`, `value`, `onValueChange`, `size`, `orientation` |
|
|
16
|
+
| `RadioGroup` / `RadioGroupItem` | Traditional radio selection | Standard Radix RadioGroup API |
|
|
17
|
+
| `Slider` | Range value selector | `value`, `onValueChange`, `min`, `max`, `step` |
|
|
18
|
+
| `Switch` | On/off toggle | `checked`, `onCheckedChange`, `disabled`, `error`, `size`, `children` |
|
|
19
|
+
| `Tooltip` | Contextual hover information | `message`, `placement`, `children` |
|
|
20
|
+
|
|
21
|
+
## Button
|
|
22
|
+
|
|
23
|
+
The most commonly used component. Always use `Button` for clickable actions.
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { Button } from '@freightos/freightwind';
|
|
27
|
+
|
|
28
|
+
// Primary action
|
|
29
|
+
<Button variant="default" size="md">Submit</Button>
|
|
30
|
+
|
|
31
|
+
// Secondary action
|
|
32
|
+
<Button variant="secondary">Cancel</Button>
|
|
33
|
+
|
|
34
|
+
// Danger action
|
|
35
|
+
<Button variant="danger">Delete</Button>
|
|
36
|
+
|
|
37
|
+
// Icon-only button (tooltip is required)
|
|
38
|
+
<Button icon="search" tooltip="Search" />
|
|
39
|
+
|
|
40
|
+
// Loading state
|
|
41
|
+
<Button loading>Saving...</Button>
|
|
42
|
+
|
|
43
|
+
// Toggle button
|
|
44
|
+
<Button variant="toggle" active={isActive}>Grid View</Button>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Variants**: `default` (primary blue), `secondary` (outlined blue), `tertiary` (text-only blue), `link` (text link), `danger` (red), `danger-ghost` (outlined red), `toggle` (gray border, blue when active).
|
|
48
|
+
|
|
49
|
+
**Sizes**: `sm` (24px height), `md` (32px height, default), `lg` (40px height).
|
|
50
|
+
|
|
51
|
+
**Icon-only buttons** must have a `tooltip` prop for accessibility. Pass icon names in kebab-case: `icon="search"`, `icon="close"`, `icon="settings"`.
|
|
52
|
+
|
|
53
|
+
## Alert
|
|
54
|
+
|
|
55
|
+
Contextual feedback banner with icon, optional title, message, and close button.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { Alert } from '@freightos/freightwind';
|
|
59
|
+
|
|
60
|
+
// Simple message-only alert
|
|
61
|
+
<Alert variant="info" message="Your shipment has been updated." />
|
|
62
|
+
|
|
63
|
+
// Alert with title
|
|
64
|
+
<Alert variant="warning" title="Attention" message="Rate expires in 24 hours." />
|
|
65
|
+
|
|
66
|
+
// Closable alert
|
|
67
|
+
<Alert variant="success" message="Saved!" closable onClose={() => {}} />
|
|
68
|
+
|
|
69
|
+
// Alert with call-to-action (only when no title)
|
|
70
|
+
<Alert variant="error" message="Connection failed." callToAction={<Button size="sm" variant="danger">Retry</Button>} />
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Variants**: `info` (blue), `success` (green), `warning` (yellow), `error` (red), `special` (purple).
|
|
74
|
+
|
|
75
|
+
## Avatar
|
|
76
|
+
|
|
77
|
+
User or entity representation. Content priority: `src` > `flag` > `title` > `icon` > default user placeholder.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { Avatar } from '@freightos/freightwind';
|
|
81
|
+
|
|
82
|
+
// Image avatar
|
|
83
|
+
<Avatar src="https://example.com/photo.jpg" alt="John" />
|
|
84
|
+
|
|
85
|
+
// Country flag
|
|
86
|
+
<Avatar flag="US" />
|
|
87
|
+
|
|
88
|
+
// Initial letter
|
|
89
|
+
<Avatar title="John Doe" />
|
|
90
|
+
|
|
91
|
+
// Icon avatar
|
|
92
|
+
<Avatar icon="settings" variant="info" />
|
|
93
|
+
|
|
94
|
+
// Sized avatar
|
|
95
|
+
<Avatar title="JD" size="lg" bordered />
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Sizes**: `xs` (16px), `sm` (24px), `md` (32px, default), `lg` (40px), `xl` (58px).
|
|
99
|
+
|
|
100
|
+
**Variants**: `default` (purple), `info` (blue), `secondary` (light blue), `warning` (yellow), `neutral` (gray), `positive` (green), `filled` (white bg), `ghost` (transparent).
|
|
101
|
+
|
|
102
|
+
## Badge
|
|
103
|
+
|
|
104
|
+
Numeric indicator or status dot. Can wrap a child element for positioning.
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import { Badge } from '@freightos/freightwind';
|
|
108
|
+
|
|
109
|
+
// Standalone count badge
|
|
110
|
+
<Badge value={5} />
|
|
111
|
+
|
|
112
|
+
// Badge wrapping an icon/button
|
|
113
|
+
<Badge value={3}><IconBell size={20} /></Badge>
|
|
114
|
+
|
|
115
|
+
// Status dot with text
|
|
116
|
+
<Badge status="success" text="Active" />
|
|
117
|
+
|
|
118
|
+
// Numbers over 99 display as "99+"
|
|
119
|
+
<Badge value={150} />
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Checkbox
|
|
123
|
+
|
|
124
|
+
Boolean or indeterminate toggle with optional label.
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
import { Checkbox } from '@freightos/freightwind';
|
|
128
|
+
|
|
129
|
+
// With label
|
|
130
|
+
<Checkbox checked={value} onCheckedChange={setValue}>Accept terms</Checkbox>
|
|
131
|
+
|
|
132
|
+
// Without label
|
|
133
|
+
<Checkbox checked={value} onCheckedChange={setValue} />
|
|
134
|
+
|
|
135
|
+
// Indeterminate state
|
|
136
|
+
<Checkbox checked="indeterminate" onCheckedChange={setValue}>Select all</Checkbox>
|
|
137
|
+
|
|
138
|
+
// Error state
|
|
139
|
+
<Checkbox error>Required field</Checkbox>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Chip
|
|
143
|
+
|
|
144
|
+
Compact label with optional icon, badge, and close button.
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { Chip } from '@freightos/freightwind';
|
|
148
|
+
|
|
149
|
+
// Basic chip
|
|
150
|
+
<Chip>Active</Chip>
|
|
151
|
+
|
|
152
|
+
// With variant and icon
|
|
153
|
+
<Chip variant="success" icon="check-circled">Approved</Chip>
|
|
154
|
+
|
|
155
|
+
// With badge count
|
|
156
|
+
<Chip variant="info" badge={3}>Notifications</Chip>
|
|
157
|
+
|
|
158
|
+
// Closable
|
|
159
|
+
<Chip closable onClose={() => {}}>Filter</Chip>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Variants**: `default` (purple), `neutral` (gray), `info` (light blue), `highlight` (bright blue), `warning` (yellow), `success` (green), `notice` (light red), `error` (red).
|
|
163
|
+
|
|
164
|
+
## Switch
|
|
165
|
+
|
|
166
|
+
On/off toggle with optional label.
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import { Switch } from '@freightos/freightwind';
|
|
170
|
+
|
|
171
|
+
// With label
|
|
172
|
+
<Switch checked={value} onCheckedChange={setValue}>Enable notifications</Switch>
|
|
173
|
+
|
|
174
|
+
// Without label
|
|
175
|
+
<Switch checked={value} onCheckedChange={setValue} />
|
|
176
|
+
|
|
177
|
+
// Sizes
|
|
178
|
+
<Switch size="sm">Small switch</Switch>
|
|
179
|
+
<Switch size="lg">Large switch</Switch>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## RadioButtonGroup
|
|
183
|
+
|
|
184
|
+
Segmented button selector. Use for choosing between 2-5 mutually exclusive options.
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import { RadioButtonGroup } from '@freightos/freightwind';
|
|
188
|
+
|
|
189
|
+
<RadioButtonGroup
|
|
190
|
+
options={[
|
|
191
|
+
{ value: 'list', label: 'List' },
|
|
192
|
+
{ value: 'grid', label: 'Grid' },
|
|
193
|
+
{ value: 'map', label: 'Map' },
|
|
194
|
+
]}
|
|
195
|
+
value={view}
|
|
196
|
+
onValueChange={setView}
|
|
197
|
+
size="md"
|
|
198
|
+
/>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Slider
|
|
202
|
+
|
|
203
|
+
Range value selector.
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
import { Slider } from '@freightos/freightwind';
|
|
207
|
+
|
|
208
|
+
<Slider value={[50]} onValueChange={setValue} min={0} max={100} step={1} />
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Tooltip
|
|
212
|
+
|
|
213
|
+
Hover tooltip. Wraps any element.
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
import { Tooltip } from '@freightos/freightwind';
|
|
217
|
+
|
|
218
|
+
<Tooltip message="Click to save" placement="top">
|
|
219
|
+
<Button>Save</Button>
|
|
220
|
+
</Tooltip>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Placements**: `top`, `bottom`, `start`, `end`, `top-start`, `top-end`, `bottom-start`, `bottom-end`.
|
|
224
|
+
|
|
225
|
+
## PopConfirm
|
|
226
|
+
|
|
227
|
+
Confirmation popover. Wraps a trigger element.
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
import { PopConfirm } from '@freightos/freightwind';
|
|
231
|
+
|
|
232
|
+
<PopConfirm title="Are you sure you want to delete this?" onConfirm={handleDelete}>
|
|
233
|
+
<Button variant="danger">Delete</Button>
|
|
234
|
+
</PopConfirm>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## MessageProvider / message
|
|
238
|
+
|
|
239
|
+
Toast notification system. Add `MessageProvider` once at the app root, then call `message.*()` anywhere.
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
import { MessageProvider, message } from '@freightos/freightwind';
|
|
243
|
+
|
|
244
|
+
// In your root layout:
|
|
245
|
+
<MessageProvider />
|
|
246
|
+
|
|
247
|
+
// Anywhere in your app:
|
|
248
|
+
message.success('Shipment created successfully');
|
|
249
|
+
message.error('Failed to save changes');
|
|
250
|
+
message.warning('Rate expires soon');
|
|
251
|
+
message.info('New update available');
|
|
252
|
+
```
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Icons
|
|
2
|
+
|
|
3
|
+
FreightWind uses `@freightos/icons` as a peer dependency for all iconography.
|
|
4
|
+
|
|
5
|
+
## Direct icon imports
|
|
6
|
+
|
|
7
|
+
For standalone icon usage, import directly from `@freightos/icons`:
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { IconSearch, IconClose, IconUser, IconRisk } from '@freightos/icons';
|
|
11
|
+
|
|
12
|
+
<IconSearch size={16} />
|
|
13
|
+
<IconClose size={20} className="text-fds-gray-80" />
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
All icons accept `size` (number, in px) and `className` props. Icons inherit `currentColor` by default.
|
|
17
|
+
|
|
18
|
+
## Icon prop on components
|
|
19
|
+
|
|
20
|
+
Components like `Button`, `Avatar`, and `Chip` accept an `icon` prop with a kebab-case name string:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
<Button icon="search" tooltip="Search" />
|
|
24
|
+
<Avatar icon="settings" />
|
|
25
|
+
<Chip icon="check-circled">Done</Chip>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Do NOT pass React components to the `icon` prop — use the kebab-case string name.
|
|
29
|
+
|
|
30
|
+
## Common icon names
|
|
31
|
+
|
|
32
|
+
| Name | Component | Usage |
|
|
33
|
+
|------|-----------|-------|
|
|
34
|
+
| `search` | `IconSearch` | Search actions |
|
|
35
|
+
| `close` | `IconClose` | Close/dismiss actions |
|
|
36
|
+
| `user` | `IconUser` | User profiles |
|
|
37
|
+
| `settings` | `IconSettings` | Settings/gear |
|
|
38
|
+
| `risk` | `IconRisk` | Warning/caution |
|
|
39
|
+
| `check-circled` | `IconCheckCircled` | Success/confirmation |
|
|
40
|
+
| `info-circled` | `IconInfoCircled` | Information |
|
|
41
|
+
| `negative-circled` | `IconNegativeCircled` | Error/negative |
|
|
42
|
+
| `arrow-down` | `IconArrowDown` | Directional |
|
|
43
|
+
| `arrow-up` | `IconArrowUp` | Directional |
|
|
44
|
+
| `edit` | `IconEdit` | Edit actions |
|
|
45
|
+
| `delete` | `IconDelete` | Delete actions |
|
|
46
|
+
| `download` | `IconDownload` | Download actions |
|
|
47
|
+
| `upload` | `IconUpload` | Upload actions |
|
|
48
|
+
| `bookmark` | `IconBookmark` | Bookmark/save |
|
|
49
|
+
|
|
50
|
+
## Icon style
|
|
51
|
+
|
|
52
|
+
All icons use stroke-only outlines (`stroke="currentColor"`). They are designed at 24px but scale cleanly to any size.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freightos/freightwind",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "FreightWind Design System — React UI components for Freightos applications",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"dist",
|
|
20
20
|
"tokens.css",
|
|
21
|
+
"guidelines",
|
|
21
22
|
"README.md",
|
|
22
23
|
"LICENSE"
|
|
23
24
|
],
|
package/tokens.css
CHANGED
|
@@ -385,3 +385,25 @@
|
|
|
385
385
|
@utility text-fds-h6 {
|
|
386
386
|
font-size: var(--text-fds-h6);
|
|
387
387
|
}
|
|
388
|
+
|
|
389
|
+
/* Scoped preflight for FreightWind components.
|
|
390
|
+
Apply to native form elements (<button>, <input>, <textarea>, <select>)
|
|
391
|
+
so components work without global Tailwind preflight. */
|
|
392
|
+
@utility fw-base {
|
|
393
|
+
box-sizing: border-box;
|
|
394
|
+
margin: 0;
|
|
395
|
+
padding: 0;
|
|
396
|
+
border: 0 solid var(--border, currentColor);
|
|
397
|
+
font: inherit;
|
|
398
|
+
font-feature-settings: inherit;
|
|
399
|
+
font-variation-settings: inherit;
|
|
400
|
+
letter-spacing: inherit;
|
|
401
|
+
color: inherit;
|
|
402
|
+
background-color: transparent;
|
|
403
|
+
border-radius: 0;
|
|
404
|
+
line-height: inherit;
|
|
405
|
+
-webkit-appearance: none;
|
|
406
|
+
appearance: none;
|
|
407
|
+
-webkit-font-smoothing: antialiased;
|
|
408
|
+
-moz-osx-font-smoothing: grayscale;
|
|
409
|
+
}
|