@freightos/freightwind 2.1.2 → 2.1.4

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.
@@ -1,6 +1,5 @@
1
1
  import * as React from 'react';
2
- let counter = 0;
3
2
  export function useStableId(prefix = 'fw') {
4
- const [id] = React.useState(() => `${prefix}-${++counter}`);
5
- return id;
3
+ const id = React.useId();
4
+ return `${prefix}${id}`;
6
5
  }
@@ -10,7 +10,7 @@ export interface ChipProps {
10
10
  children?: string;
11
11
  }
12
12
  declare const chipVariants: (props?: ({
13
- variant?: "default" | "info" | "success" | "warning" | "error" | "neutral" | "highlight" | "notice" | null | undefined;
13
+ variant?: "default" | "success" | "error" | "info" | "warning" | "neutral" | "highlight" | "notice" | null | undefined;
14
14
  } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
15
15
  declare function Chip({ variant, icon, closable, onClose, badge, children, }: ChipProps): import("react/jsx-runtime").JSX.Element | null;
16
16
  export { Chip, chipVariants };
@@ -0,0 +1,66 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ import { type VariantProps } from 'class-variance-authority';
3
+ export type UploadVariant = 'rectangle' | 'square';
4
+ export type ForbiddenReason = 'multiple' | 'type' | 'size' | 'extension' | 'custom';
5
+ export type UploadFile = File | {
6
+ name: string;
7
+ };
8
+ export type UploadTranslator = (key: string) => string;
9
+ declare const uploadVariants: (props?: ({
10
+ variant?: "square" | "rectangle" | null | undefined;
11
+ state?: "default" | "disabled" | "forbidden" | "uploading" | "uploading-spinner" | "success" | "error" | null | undefined;
12
+ } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
13
+ export interface UploadProps extends Omit<HTMLAttributes<HTMLLabelElement>, 'onChange'>, VariantProps<typeof uploadVariants> {
14
+ /** Test automation identifier. Rendered as `data-test-id` on the root and propagated to inner DOM nodes. */
15
+ dataTestId: string;
16
+ /** Layout variant. */
17
+ variant?: UploadVariant;
18
+ /** Single associated file (controlled). Pass `{ name: string }` for SSR display-only previews. */
19
+ value?: UploadFile | null;
20
+ /** Multiple associated files (controlled). Overrides `value` when provided. */
21
+ files?: UploadFile[];
22
+ /** Max number of files allowed. Omit or set to 1 for single-file mode. Exceeding this → forbidden. */
23
+ maxFiles?: number;
24
+ /** MIME types / extensions accepted. Pass an array, e.g. `['application/pdf', '.png']`. */
25
+ accept?: string[];
26
+ /** Max bytes per file. Exceeding this → forbidden. */
27
+ maxSize?: number;
28
+ /** Drives the error visual state (red frame + red icon). */
29
+ isError?: boolean;
30
+ /** Disables the surface (gray border, no interaction, no remove allowed). */
31
+ isDisabled?: boolean;
32
+ /** Stretch the surface to fill its parent's width and height. */
33
+ isFillContainer?: boolean;
34
+ /** Override copy per state. Pass any subset; falls back to defaults. */
35
+ labels?: Partial<{
36
+ idleTitle: string;
37
+ idleSubtitle: string;
38
+ idleCompact: string;
39
+ uploading: string;
40
+ forbidden: Partial<Record<ForbiddenReason, {
41
+ title: string;
42
+ subtitle: string;
43
+ compact?: string;
44
+ }>>;
45
+ }>;
46
+ /** Optional translation function for all built-in copy. Defaults to identity. */
47
+ t?: UploadTranslator;
48
+ /** Fires when one or more files are selected or dropped. The component flips to `uploading` after this fires; call `setIsUploading(false)` when your async upload completes. Pass an optional `progress` value (0–1) to drive the determinate progress bar; omit it for the indeterminate animation. */
49
+ onUpload?: (files: File[], setIsUploading: (v: boolean, progress?: number) => void) => void;
50
+ /** Fires when the trash button is clicked. Removes ALL files. */
51
+ onRemove?: () => void;
52
+ /** Fires when an invalid drag/drop is detected. */
53
+ onForbidden?: (reason: ForbiddenReason) => void;
54
+ /** A11y label override for the surface. */
55
+ ariaLabel?: string;
56
+ /** Seed the internal uploading state on mount. Useful for static demos and Storybook. */
57
+ defaultUploading?: boolean;
58
+ /** Seed the initial progress value (0–1) when defaultUploading is true. Disables the indeterminate animation and shows a static determinate bar. */
59
+ defaultUploadProgress?: number;
60
+ /** Seed the forbidden state on mount with a specific reason. Useful for static demos and Storybook. */
61
+ defaultForbidden?: ForbiddenReason;
62
+ /** Controls the uploading display: 'progress' = text + progress bar (default); 'spinner' = 32×32 spinner only (no text — enforced for square too). */
63
+ uploadingStyle?: 'progress' | 'spinner';
64
+ }
65
+ export declare const Upload: import("react").ForwardRefExoticComponent<UploadProps & import("react").RefAttributes<HTMLLabelElement>>;
66
+ export { uploadVariants };
@@ -2,3 +2,5 @@ export { cn } from './lib/utils';
2
2
  export { useStableId } from './lib/use-stable-id';
3
3
  export { iconMap, renderInputIcon } from './lib/icon-utils';
4
4
  export type { IconName, InputShellSize } from './lib/icon-utils';
5
+ export { Upload, uploadVariants } from './components/upload';
6
+ export type { UploadProps, UploadVariant, UploadFile, ForbiddenReason, UploadTranslator } from './components/upload';
@@ -0,0 +1,52 @@
1
+ /**
2
+ * MIME-type → extensions map sourced from the IANA / Apache / Nginx MIME database
3
+ * (https://github.com/jshttp/mime-db). Keys are canonical MIME types
4
+ * (e.g. `image/aces`); values are the extensions that legitimately produce that
5
+ * type, without a leading dot and in lowercase.
6
+ *
7
+ * Used to validate uploaded files: reject when the browser-reported `File.type`
8
+ * is unknown, **and** cross-check that the filename's extension matches the set
9
+ * registered for that type. This catches "extension spoofing" — e.g. a `.exe`
10
+ * renamed to `.jpg` will report `application/x-msdownload` (or empty), which
11
+ * does not list `jpg`, so it fails validation.
12
+ */
13
+ export declare const VALID_MIME_TYPES: Readonly<Record<string, readonly string[]>>;
14
+ /**
15
+ * Flat set of every extension known to {@link VALID_MIME_TYPES}, used by the
16
+ * extension-only fast path ({@link hasValidExtension}). Derived once at module
17
+ * load — kept in sync with the map automatically.
18
+ */
19
+ export declare const VALID_FILE_EXTENSIONS: ReadonlySet<string>;
20
+ /**
21
+ * Normalize a filename or raw extension to a lowercase no-dot extension token.
22
+ * Returns null if no extension is present.
23
+ */
24
+ export declare function normalizeExtension(input: string): string | null;
25
+ /**
26
+ * Returns true when the given filename ends in a recognized extension.
27
+ * Files with no extension, or extensions that aren't in {@link VALID_FILE_EXTENSIONS},
28
+ * are rejected as "fake" / nonsense.
29
+ *
30
+ * Prefer {@link validateFileType} when a `File` object is available — it catches
31
+ * extension spoofing that this function cannot.
32
+ */
33
+ export declare function hasValidExtension(filename: string): boolean;
34
+ /**
35
+ * Strict file validation: the browser-reported MIME type must be known, AND the
36
+ * filename's extension must be one of the extensions registered for that type.
37
+ *
38
+ * Rejects:
39
+ * - files with no extension
40
+ * - files whose `type` is empty or unknown to {@link VALID_MIME_TYPES}
41
+ * - files whose extension does not match the registered list for `file.type`
42
+ * (extension-spoofing guard, e.g. `.exe` renamed to `.jpg`)
43
+ *
44
+ * `file.type` comparison is case-insensitive; some browsers lowercase but a few
45
+ * legacy ones don't.
46
+ */
47
+ export declare function validateFileType(file: File): boolean;
48
+ /**
49
+ * Look up every MIME type that legitimately produces the given extension.
50
+ * Useful for `accept`-attribute construction or reverse diagnostics.
51
+ */
52
+ export declare function getMimeTypesForExtension(extension: string): readonly string[];
@@ -1,233 +1,233 @@
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/freightwind/icons tailwindcss @tailwindcss/vite
13
- ```
14
-
15
- `@freightos/freightwind/icons` is a required peer dependency that provides all iconography. Tailwind CSS v4 is required for component styling.
16
-
17
- ### Step 2 — Configure Tailwind CSS v4
18
-
19
- FreightWind requires Tailwind CSS v4. Set it up with your bundler:
20
-
21
- **Vite** — add the Tailwind plugin to `vite.config.ts`:
22
-
23
- ```ts
24
- import tailwindcss from '@tailwindcss/vite';
25
- import { defineConfig } from 'vite';
26
- import react from '@vitejs/plugin-react';
27
-
28
- export default defineConfig({
29
- plugins: [react(), tailwindcss()],
30
- });
31
- ```
32
-
33
- ### Step 3 — Set up your global CSS file
34
-
35
- **CRITICAL**: Your main CSS file must import Tailwind AND FreightWind tokens in the correct order. The tokens.css file uses Tailwind v4's `@theme inline` directive to register color utilities (like `bg-fds-blue-30`, `text-fds-gray-80`, etc.) — this ONLY works when imported inside a CSS file that Tailwind processes.
36
-
37
- Create or update your main CSS file (e.g. `src/index.css` or `src/styles/globals.css`):
38
-
39
- ```css
40
- /* Step A: Import Tailwind CSS — this MUST come first */
41
- @import "tailwindcss";
42
-
43
- /* Step B: Import FreightWind tokens — MUST be in this same CSS file, AFTER tailwindcss.
44
- This registers all FDS color utilities (bg-fds-blue-30, text-fds-gray-80, etc.),
45
- spacing tokens, typography tokens, and custom utilities used by components.
46
-
47
- DO NOT import this as a <link> tag in HTML — it must be processed by Tailwind's
48
- CSS engine for @theme inline to work. */
49
- @import "@freightos/freightwind/tokens.css";
50
-
51
- /* Step C: Global base styles — REQUIRED for components to render correctly */
52
- * {
53
- -webkit-font-smoothing: antialiased;
54
- -moz-osx-font-smoothing: grayscale;
55
- }
56
-
57
- body {
58
- background-color: var(--ground);
59
- color: var(--foreground);
60
- font-family: 'Open Sans', ui-sans-serif, system-ui, -apple-system, sans-serif;
61
- font-size: 0.875rem; /* 14px — FDS base font size */
62
- line-height: 1.5;
63
- }
64
- ```
65
-
66
- **IMPORTANT NOTES:**
67
- - `@import "@freightos/freightwind/tokens.css"` MUST be inside the same CSS file as `@import "tailwindcss"` — never in a separate file or `<link>` tag
68
- - The import order matters: `tailwindcss` first, then `tokens.css`
69
- - If tokens are imported separately or in the wrong order, Tailwind utility classes like `bg-fds-purple-2`, `bg-fds-gray-20`, `text-fds-blue-30`, etc. will NOT work and components will have missing styles
70
-
71
- ### Step 4 — Load the Open Sans font
72
-
73
- FreightWind uses **Open Sans** as its font family. Load weights 400, 600, and 700 from Google Fonts.
74
-
75
- **Option A — In your HTML `<head>` (simplest):**
76
-
77
- ```html
78
- <link rel="preconnect" href="https://fonts.googleapis.com" />
79
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
80
- <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap" rel="stylesheet" />
81
- ```
82
-
83
- **Option B — In Next.js with `next/font`:**
84
-
85
- ```tsx
86
- import { Open_Sans } from 'next/font/google';
87
-
88
- const openSans = Open_Sans({
89
- subsets: ['latin'],
90
- weight: ['400', '600', '700'],
91
- variable: '--font-open-sans',
92
- });
93
-
94
- // Apply to <html> or <body>:
95
- <html className={openSans.variable}>
96
- ```
97
-
98
- Then in your CSS, override the font-sans variable:
99
-
100
- ```css
101
- @theme inline {
102
- --font-sans: var(--font-open-sans), 'Open Sans', ui-sans-serif, system-ui, sans-serif;
103
- }
104
- ```
105
-
106
- ### Step 5 — Import and use components
107
-
108
- ```tsx
109
- import { Button, Alert, Checkbox } from '@freightos/freightwind';
110
- import { IconSearch } from '@freightos/freightwind/icons';
111
-
112
- <Button variant="default" size="md">Submit</Button>
113
- <Alert variant="info" message="Shipment updated." />
114
- <Checkbox>Accept terms</Checkbox>
115
- ```
116
-
117
- ### Step 6 — Toast notifications (optional)
118
-
119
- If using the `message` toast API, wrap your app with `MessageProvider`:
120
-
121
- ```tsx
122
- import { MessageProvider, message } from '@freightos/freightwind';
123
-
124
- // In your root layout:
125
- <MessageProvider />
126
-
127
- // Anywhere in your app:
128
- message.success('Saved successfully');
129
- ```
130
-
131
- ## Complete minimal example
132
-
133
- Here is a full working `src/index.css`:
134
-
135
- ```css
136
- @import "tailwindcss";
137
- @import "@freightos/freightwind/tokens.css";
138
-
139
- * {
140
- -webkit-font-smoothing: antialiased;
141
- -moz-osx-font-smoothing: grayscale;
142
- }
143
-
144
- body {
145
- background-color: var(--ground);
146
- color: var(--foreground);
147
- font-family: 'Open Sans', ui-sans-serif, system-ui, -apple-system, sans-serif;
148
- font-size: 0.875rem;
149
- line-height: 1.5;
150
- }
151
- ```
152
-
153
- And a full working `src/App.tsx`:
154
-
155
- ```tsx
156
- import { Button, Alert, Chip, Switch, Checkbox } from '@freightos/freightwind';
157
- import { IconSearch } from '@freightos/freightwind/icons';
158
-
159
- function App() {
160
- return (
161
- <div className="p-fds-xl">
162
- <h1 className="text-fds-h3 font-fds-bold leading-fds-title mb-fds-lg">My App</h1>
163
- <Alert variant="info" message="Welcome to the app!" />
164
- <div className="mt-fds-lg flex gap-fds-sm">
165
- <Button variant="default">Primary</Button>
166
- <Button variant="secondary">Secondary</Button>
167
- <Button icon="search" tooltip="Search" />
168
- </div>
169
- <div className="mt-fds-lg flex gap-fds-md items-center">
170
- <Chip variant="success">Active</Chip>
171
- <Switch>Enable notifications</Switch>
172
- <Checkbox>Accept terms</Checkbox>
173
- </div>
174
- </div>
175
- );
176
- }
177
- ```
178
-
179
- ## Troubleshooting
180
-
181
- ### Tailwind utility classes not working (e.g. `bg-fds-purple-2` has no effect)
182
-
183
- This means `tokens.css` is not being processed by Tailwind. Make sure:
184
- 1. `@import "@freightos/freightwind/tokens.css"` is in the SAME CSS file as `@import "tailwindcss"`
185
- 2. It comes AFTER the tailwindcss import
186
- 3. It is NOT loaded via a `<link>` tag — it must be in CSS `@import` so Tailwind processes the `@theme inline` block
187
-
188
- ### Colors like `--ground`, `--foreground`, `--card` are undefined
189
-
190
- These CSS variables are defined in the `:root` block inside `tokens.css`. Make sure `tokens.css` is imported.
191
-
192
- ### Font looks wrong
193
-
194
- Make sure Open Sans is loaded (Step 4) and the body `font-family` is set (Step 3C).
195
-
196
- ### Text rendering looks rough or inconsistent
197
-
198
- Add global font smoothing (Step 3C):
199
- ```css
200
- * {
201
- -webkit-font-smoothing: antialiased;
202
- -moz-osx-font-smoothing: grayscale;
203
- }
204
- ```
205
-
206
- ## Dark mode
207
-
208
- 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.
209
-
210
- ## Available components
211
-
212
- See `overview-components.md` for a full list of components with their props and usage.
213
-
214
- ## Available icons
215
-
216
- See `overview-icons.md` for details on the icon system.
217
-
218
- ## Design tokens
219
-
220
- FreightWind uses a custom token system instead of Tailwind defaults:
221
-
222
- - **Colors**: `design-tokens/colors.md` — semantic color palette (blue, red, green, yellow, gray, purple)
223
- - **Typography**: `design-tokens/typography.md` — font family, sizes, weights, and line heights
224
- - **Spacing**: `design-tokens/spacing.md` — consistent spacing scale
225
-
226
- ## Key conventions
227
-
228
- - Always use FDS tokens (`text-fds-*`, `p-fds-*`, `rounded-fds-*`, `bg-fds-*`) instead of Tailwind defaults
229
- - All components support both controlled and uncontrolled usage patterns
230
- - Components with `icon` props accept kebab-case icon names: `"search"`, `"close"`, `"user"`, `"risk"`, etc.
231
- - Use `cn()` utility (exported from the package) for className merging
232
- - The page canvas background should be `bg-ground` (#f6f6f6), not white
233
- - Cards and content areas use `bg-card` (#ffffff) against the ground
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/freightwind/icons tailwindcss @tailwindcss/vite
13
+ ```
14
+
15
+ `@freightos/freightwind/icons` is a required peer dependency that provides all iconography. Tailwind CSS v4 is required for component styling.
16
+
17
+ ### Step 2 — Configure Tailwind CSS v4
18
+
19
+ FreightWind requires Tailwind CSS v4. Set it up with your bundler:
20
+
21
+ **Vite** — add the Tailwind plugin to `vite.config.ts`:
22
+
23
+ ```ts
24
+ import tailwindcss from '@tailwindcss/vite';
25
+ import { defineConfig } from 'vite';
26
+ import react from '@vitejs/plugin-react';
27
+
28
+ export default defineConfig({
29
+ plugins: [react(), tailwindcss()],
30
+ });
31
+ ```
32
+
33
+ ### Step 3 — Set up your global CSS file
34
+
35
+ **CRITICAL**: Your main CSS file must import Tailwind AND FreightWind tokens in the correct order. The tokens.css file uses Tailwind v4's `@theme inline` directive to register color utilities (like `bg-fds-blue-30`, `text-fds-gray-80`, etc.) — this ONLY works when imported inside a CSS file that Tailwind processes.
36
+
37
+ Create or update your main CSS file (e.g. `src/index.css` or `src/styles/globals.css`):
38
+
39
+ ```css
40
+ /* Step A: Import Tailwind CSS — this MUST come first */
41
+ @import "tailwindcss";
42
+
43
+ /* Step B: Import FreightWind tokens — MUST be in this same CSS file, AFTER tailwindcss.
44
+ This registers all FDS color utilities (bg-fds-blue-30, text-fds-gray-80, etc.),
45
+ spacing tokens, typography tokens, and custom utilities used by components.
46
+
47
+ DO NOT import this as a <link> tag in HTML — it must be processed by Tailwind's
48
+ CSS engine for @theme inline to work. */
49
+ @import "@freightos/freightwind/tokens.css";
50
+
51
+ /* Step C: Global base styles — REQUIRED for components to render correctly */
52
+ * {
53
+ -webkit-font-smoothing: antialiased;
54
+ -moz-osx-font-smoothing: grayscale;
55
+ }
56
+
57
+ body {
58
+ background-color: var(--ground);
59
+ color: var(--foreground);
60
+ font-family: 'Open Sans', ui-sans-serif, system-ui, -apple-system, sans-serif;
61
+ font-size: 0.875rem; /* 14px — FDS base font size */
62
+ line-height: 1.5;
63
+ }
64
+ ```
65
+
66
+ **IMPORTANT NOTES:**
67
+ - `@import "@freightos/freightwind/tokens.css"` MUST be inside the same CSS file as `@import "tailwindcss"` — never in a separate file or `<link>` tag
68
+ - The import order matters: `tailwindcss` first, then `tokens.css`
69
+ - If tokens are imported separately or in the wrong order, Tailwind utility classes like `bg-fds-purple-2`, `bg-fds-gray-20`, `text-fds-blue-30`, etc. will NOT work and components will have missing styles
70
+
71
+ ### Step 4 — Load the Open Sans font
72
+
73
+ FreightWind uses **Open Sans** as its font family. Load weights 400, 600, and 700 from Google Fonts.
74
+
75
+ **Option A — In your HTML `<head>` (simplest):**
76
+
77
+ ```html
78
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
79
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
80
+ <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap" rel="stylesheet" />
81
+ ```
82
+
83
+ **Option B — In Next.js with `next/font`:**
84
+
85
+ ```tsx
86
+ import { Open_Sans } from 'next/font/google';
87
+
88
+ const openSans = Open_Sans({
89
+ subsets: ['latin'],
90
+ weight: ['400', '600', '700'],
91
+ variable: '--font-open-sans',
92
+ });
93
+
94
+ // Apply to <html> or <body>:
95
+ <html className={openSans.variable}>
96
+ ```
97
+
98
+ Then in your CSS, override the font-sans variable:
99
+
100
+ ```css
101
+ @theme inline {
102
+ --font-sans: var(--font-open-sans), 'Open Sans', ui-sans-serif, system-ui, sans-serif;
103
+ }
104
+ ```
105
+
106
+ ### Step 5 — Import and use components
107
+
108
+ ```tsx
109
+ import { Button, Alert, Checkbox } from '@freightos/freightwind';
110
+ import { IconSearch } from '@freightos/freightwind/icons';
111
+
112
+ <Button variant="default" size="md">Submit</Button>
113
+ <Alert variant="info" message="Shipment updated." />
114
+ <Checkbox>Accept terms</Checkbox>
115
+ ```
116
+
117
+ ### Step 6 — Toast notifications (optional)
118
+
119
+ If using the `message` toast API, wrap your app with `MessageProvider`:
120
+
121
+ ```tsx
122
+ import { MessageProvider, message } from '@freightos/freightwind';
123
+
124
+ // In your root layout:
125
+ <MessageProvider />
126
+
127
+ // Anywhere in your app:
128
+ message.success('Saved successfully');
129
+ ```
130
+
131
+ ## Complete minimal example
132
+
133
+ Here is a full working `src/index.css`:
134
+
135
+ ```css
136
+ @import "tailwindcss";
137
+ @import "@freightos/freightwind/tokens.css";
138
+
139
+ * {
140
+ -webkit-font-smoothing: antialiased;
141
+ -moz-osx-font-smoothing: grayscale;
142
+ }
143
+
144
+ body {
145
+ background-color: var(--ground);
146
+ color: var(--foreground);
147
+ font-family: 'Open Sans', ui-sans-serif, system-ui, -apple-system, sans-serif;
148
+ font-size: 0.875rem;
149
+ line-height: 1.5;
150
+ }
151
+ ```
152
+
153
+ And a full working `src/App.tsx`:
154
+
155
+ ```tsx
156
+ import { Button, Alert, Chip, Switch, Checkbox } from '@freightos/freightwind';
157
+ import { IconSearch } from '@freightos/freightwind/icons';
158
+
159
+ function App() {
160
+ return (
161
+ <div className="p-fds-xl">
162
+ <h1 className="text-fds-h3 font-fds-bold leading-fds-title mb-fds-lg">My App</h1>
163
+ <Alert variant="info" message="Welcome to the app!" />
164
+ <div className="mt-fds-lg flex gap-fds-sm">
165
+ <Button variant="default">Primary</Button>
166
+ <Button variant="secondary">Secondary</Button>
167
+ <Button icon="search" tooltip="Search" />
168
+ </div>
169
+ <div className="mt-fds-lg flex gap-fds-md items-center">
170
+ <Chip variant="success">Active</Chip>
171
+ <Switch>Enable notifications</Switch>
172
+ <Checkbox>Accept terms</Checkbox>
173
+ </div>
174
+ </div>
175
+ );
176
+ }
177
+ ```
178
+
179
+ ## Troubleshooting
180
+
181
+ ### Tailwind utility classes not working (e.g. `bg-fds-purple-2` has no effect)
182
+
183
+ This means `tokens.css` is not being processed by Tailwind. Make sure:
184
+ 1. `@import "@freightos/freightwind/tokens.css"` is in the SAME CSS file as `@import "tailwindcss"`
185
+ 2. It comes AFTER the tailwindcss import
186
+ 3. It is NOT loaded via a `<link>` tag — it must be in CSS `@import` so Tailwind processes the `@theme inline` block
187
+
188
+ ### Colors like `--ground`, `--foreground`, `--card` are undefined
189
+
190
+ These CSS variables are defined in the `:root` block inside `tokens.css`. Make sure `tokens.css` is imported.
191
+
192
+ ### Font looks wrong
193
+
194
+ Make sure Open Sans is loaded (Step 4) and the body `font-family` is set (Step 3C).
195
+
196
+ ### Text rendering looks rough or inconsistent
197
+
198
+ Add global font smoothing (Step 3C):
199
+ ```css
200
+ * {
201
+ -webkit-font-smoothing: antialiased;
202
+ -moz-osx-font-smoothing: grayscale;
203
+ }
204
+ ```
205
+
206
+ ## Dark mode
207
+
208
+ 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.
209
+
210
+ ## Available components
211
+
212
+ See `overview-components.md` for a full list of components with their props and usage.
213
+
214
+ ## Available icons
215
+
216
+ See `overview-icons.md` for details on the icon system.
217
+
218
+ ## Design tokens
219
+
220
+ FreightWind uses a custom token system instead of Tailwind defaults:
221
+
222
+ - **Colors**: `design-tokens/colors.md` — semantic color palette (blue, red, green, yellow, gray, purple)
223
+ - **Typography**: `design-tokens/typography.md` — font family, sizes, weights, and line heights
224
+ - **Spacing**: `design-tokens/spacing.md` — consistent spacing scale
225
+
226
+ ## Key conventions
227
+
228
+ - Always use FDS tokens (`text-fds-*`, `p-fds-*`, `rounded-fds-*`, `bg-fds-*`) instead of Tailwind defaults
229
+ - All components support both controlled and uncontrolled usage patterns
230
+ - Components with `icon` props accept kebab-case icon names: `"search"`, `"close"`, `"user"`, `"risk"`, etc.
231
+ - Use `cn()` utility (exported from the package) for className merging
232
+ - The page canvas background should be `bg-ground` (#f6f6f6), not white
233
+ - Cards and content areas use `bg-card` (#ffffff) against the ground