@hotelcard/ui 0.0.4 → 0.0.5

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/README.md CHANGED
@@ -1,283 +1,163 @@
1
1
  # @hotelcard/ui
2
2
 
3
- Shared UI component library for HotelCard applications (mobile app and website).
3
+ Shared UI components for HotelCard website and mobile app.
4
4
 
5
- ## Quick Start
5
+ [![npm version](https://img.shields.io/npm/v/@hotelcard/ui.svg)](https://www.npmjs.com/package/@hotelcard/ui)
6
+
7
+ ---
6
8
 
7
- ### Installation
9
+ ## Installation
8
10
 
9
11
  ```bash
10
- # In your project
11
12
  npm install @hotelcard/ui
12
-
13
- # Or with local development (file: protocol)
14
- "@hotelcard/ui": "file:../hotelcard-monorepo/packages/ui"
15
- ```
16
-
17
- ### Import Components
18
-
19
- ```tsx
20
- import { Button, Badge, Rating, Input, Checkbox, Modal } from '@hotelcard/ui';
21
- import type { ButtonProps, BadgeProps, InputProps } from '@hotelcard/ui';
13
+ # or
14
+ pnpm add @hotelcard/ui
15
+ # or
16
+ yarn add @hotelcard/ui
22
17
  ```
23
18
 
24
- ### Import Styles (REQUIRED)
19
+ ## Peer Dependencies
25
20
 
26
- You **must** import the CSS in your app's entry point (e.g., `main.tsx`):
21
+ Make sure you have these installed:
27
22
 
28
- ```tsx
29
- // main.tsx or App.tsx
30
- import '@hotelcard/ui/dist/index.css';
23
+ ```bash
24
+ npm install react react-dom
31
25
  ```
32
26
 
33
- **If you skip this step, components will render without styling!**
34
-
35
27
  ---
36
28
 
37
- ## Available Components
38
-
39
- ### Button
29
+ ## Quick Start
40
30
 
41
31
  ```tsx
42
- import { Button } from '@hotelcard/ui';
43
-
44
- // Primary button (default)
45
- <Button onClick={handleClick}>Submit</Button>
32
+ import { Button, Card, Badge } from '@hotelcard/ui';
46
33
 
47
- // Secondary button
48
- <Button variant="secondary">Cancel</Button>
49
-
50
- // Small size
51
- <Button size="small">Small Button</Button>
52
-
53
- // Link style
54
- <Button variant="link">Learn More</Button>
55
-
56
- // With icons
57
- <Button leftIcon={<SearchIcon />}>Search</Button>
34
+ // Import styles (required - do this once in your app entry)
35
+ import '@hotelcard/ui/dist/index.css';
58
36
 
59
- // Icon-only button
60
- <Button iconOnly><HeartIcon /></Button>
37
+ function App() {
38
+ return (
39
+ <div>
40
+ <Button variant="primary" onClick={() => alert('Clicked!')}>
41
+ Book Now
42
+ </Button>
43
+
44
+ <Card
45
+ image="https://example.com/hotel.jpg"
46
+ title="Hotel Schweizerhof"
47
+ location="Luzern, Switzerland"
48
+ stars={5}
49
+ price="CHF 195"
50
+ originalPrice="CHF 390"
51
+ onClick={() => console.log('Card clicked')}
52
+ >
53
+ <Badge color="primary" size="small">-50%</Badge>
54
+ </Card>
55
+ </div>
56
+ );
57
+ }
61
58
  ```
62
59
 
63
- **Props:**
64
- | Prop | Type | Default | Description |
65
- |------|------|---------|-------------|
66
- | `variant` | `'primary' \| 'secondary' \| 'link'` | `'primary'` | Button style |
67
- | `size` | `'small' \| 'medium'` | `'medium'` | Button size |
68
- | `iconOnly` | `boolean` | `false` | Icon-only circular button |
69
- | `leftIcon` | `ReactNode` | - | Icon on the left |
70
- | `rightIcon` | `ReactNode` | - | Icon on the right |
71
- | `disabled` | `boolean` | `false` | Disabled state |
72
-
73
- ### Badge
60
+ ---
74
61
 
75
- ```tsx
76
- import { Badge } from '@hotelcard/ui';
62
+ ## Available Components
77
63
 
78
- // Discount badge
79
- <Badge color="primary" size="small" style="heavy">
80
- -50%
81
- </Badge>
64
+ ### UI Components
65
+
66
+ | Component | Description |
67
+ |-----------|-------------|
68
+ | `Button` | Primary, secondary, link buttons with icon support |
69
+ | `Input` | Text input with label, helper text, and validation |
70
+ | `Badge` | Status and discount badges |
71
+ | `Card` | Hotel card component |
72
+ | `CompactCard` | Compact hotel card for carousels |
73
+ | `Chip` | Filter and tag chips |
74
+ | `Checkbox` | Checkbox with label |
75
+ | `RadioButton` | Radio button with label |
76
+ | `Rating` | Star rating display |
77
+ | `Divider` | Horizontal/vertical divider |
78
+ | `Dropdown` | Select dropdown |
79
+ | `Modal` | Modal dialog |
80
+ | `SectionHeader` | Section title with "Show all" button |
82
81
 
83
- // Status badge
84
- <Badge color="success" style="light">
85
- Available
86
- </Badge>
87
- ```
82
+ ### Icons
88
83
 
89
- **Props:**
90
- | Prop | Type | Default | Description |
91
- |------|------|---------|-------------|
92
- | `color` | `'primary' \| 'secondary' \| 'neutral' \| 'success' \| 'warning'` | `'primary'` | Badge color |
93
- | `size` | `'small' \| 'large'` | `'large'` | Badge size |
94
- | `style` | `'heavy' \| 'light'` | `'heavy'` | Solid or light background |
84
+ | Icon | Description |
85
+ |------|-------------|
86
+ | `HeartIcon` | Heart icon (filled/outline) |
87
+ | `StarIcon` | Star icon for ratings |
88
+ | `ChevronLeftIcon` | Left arrow |
89
+ | `ChevronRightIcon` | Right arrow |
90
+ | `PinIcon` | Location pin |
95
91
 
96
- ### Rating
92
+ ---
97
93
 
98
- ```tsx
99
- import { Rating } from '@hotelcard/ui';
94
+ ## Component Examples
100
95
 
101
- // Star rating
102
- <Rating value={4.5} />
96
+ ### Button
103
97
 
104
- // Show numeric value
105
- <Rating value={4.5} showValue />
98
+ ```tsx
99
+ import { Button } from '@hotelcard/ui';
106
100
 
107
- // Different sizes
108
- <Rating value={3} size="small" />
109
- <Rating value={3} size="large" />
101
+ <Button variant="primary">Primary</Button>
102
+ <Button variant="secondary">Secondary</Button>
103
+ <Button variant="link">Link Style</Button>
104
+ <Button size="small">Small Button</Button>
105
+ <Button disabled>Disabled</Button>
106
+ <Button leftIcon={<SearchIcon />}>Search</Button>
107
+ <Button iconOnly><HeartIcon /></Button>
110
108
  ```
111
109
 
112
- **Props:**
113
- | Prop | Type | Default | Description |
114
- |------|------|---------|-------------|
115
- | `value` | `number` | - | Rating value (0-5) |
116
- | `maxValue` | `number` | `5` | Maximum stars |
117
- | `showValue` | `boolean` | `false` | Show numeric value |
118
- | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | Star size |
119
-
120
110
  ### Input
121
111
 
122
112
  ```tsx
123
113
  import { Input } from '@hotelcard/ui';
124
114
 
125
- // Basic input
126
115
  <Input
127
116
  label="Email"
128
- placeholder="Enter your email"
117
+ type="email"
118
+ placeholder="you@example.com"
129
119
  value={email}
130
120
  onChange={setEmail}
121
+ helper="We'll never share your email"
131
122
  />
132
123
 
133
- // With helper text
134
124
  <Input
135
125
  label="Password"
136
126
  type="password"
137
- helper="Must be at least 8 characters"
138
- />
139
-
140
- // Error state
141
- <Input
142
- label="Email"
143
127
  error
144
- helper="Invalid email address"
145
- />
146
-
147
- // With icons
148
- <Input
149
- leftIcon={<SearchIcon />}
150
- placeholder="Search..."
151
- />
152
-
153
- // Disabled
154
- <Input
155
- label="Disabled"
156
- disabled
157
- value="Cannot edit"
128
+ helper="Password is required"
158
129
  />
159
130
  ```
160
131
 
161
- **Props:**
162
- | Prop | Type | Default | Description |
163
- |------|------|---------|-------------|
164
- | `label` | `string` | - | Label text above input |
165
- | `placeholder` | `string` | `'Placeholder'` | Placeholder text |
166
- | `helper` | `string` | - | Helper text below input |
167
- | `value` | `string` | - | Controlled value |
168
- | `onChange` | `(value: string) => void` | - | Change handler |
169
- | `type` | `'text' \| 'email' \| 'password' \| 'tel' \| 'number'` | `'text'` | Input type |
170
- | `error` | `boolean` | `false` | Show error state |
171
- | `disabled` | `boolean` | `false` | Disable input |
172
- | `leftIcon` | `ReactNode` | - | Icon on the left |
173
- | `rightIcon` | `ReactNode` | - | Icon on the right |
174
-
175
- ### Checkbox
176
-
177
- ```tsx
178
- import { Checkbox } from '@hotelcard/ui';
179
-
180
- // Controlled checkbox
181
- <Checkbox
182
- label="Accept terms"
183
- checked={accepted}
184
- onChange={setAccepted}
185
- />
186
-
187
- // Uncontrolled with default
188
- <Checkbox
189
- label="Remember me"
190
- defaultChecked
191
- />
192
-
193
- // Error state
194
- <Checkbox
195
- label="Required field"
196
- error
197
- />
198
-
199
- // Sizes
200
- <Checkbox label="Medium" size="medium" />
201
- <Checkbox label="Small" size="small" />
202
- ```
203
-
204
- **Props:**
205
- | Prop | Type | Default | Description |
206
- |------|------|---------|-------------|
207
- | `label` | `string` | - | Label text |
208
- | `checked` | `boolean` | - | Controlled checked state |
209
- | `defaultChecked` | `boolean` | `false` | Default checked state |
210
- | `onChange` | `(checked: boolean) => void` | - | Change handler |
211
- | `size` | `'small' \| 'medium'` | `'medium'` | Checkbox size |
212
- | `disabled` | `boolean` | `false` | Disable checkbox |
213
- | `error` | `boolean` | `false` | Show error state |
214
-
215
- ### RadioButton
132
+ ### Badge
216
133
 
217
134
  ```tsx
218
- import { RadioButton } from '@hotelcard/ui';
135
+ import { Badge } from '@hotelcard/ui';
219
136
 
220
- // Radio group
221
- <RadioButton
222
- label="Option 1"
223
- checked={selected === '1'}
224
- onChange={() => setSelected('1')}
225
- name="options"
226
- />
227
- <RadioButton
228
- label="Option 2"
229
- checked={selected === '2'}
230
- onChange={() => setSelected('2')}
231
- name="options"
232
- />
137
+ <Badge color="primary" size="small" style="heavy">-50%</Badge>
138
+ <Badge color="success" style="light">Available</Badge>
139
+ <Badge color="warning">Limited</Badge>
233
140
  ```
234
141
 
235
- **Props:**
236
- | Prop | Type | Default | Description |
237
- |------|------|---------|-------------|
238
- | `label` | `string` | - | Label text |
239
- | `checked` | `boolean` | - | Checked state (required) |
240
- | `onChange` | `(checked: boolean) => void` | - | Change handler (required) |
241
- | `name` | `string` | - | Group name for form |
242
- | `disabled` | `boolean` | `false` | Disable radio |
243
-
244
- ### Dropdown
142
+ ### Card
245
143
 
246
144
  ```tsx
247
- import { Dropdown } from '@hotelcard/ui';
248
-
249
- const options = [
250
- { value: '1', label: 'Option 1' },
251
- { value: '2', label: 'Option 2' },
252
- { value: '3', label: 'Option 3' },
253
- ];
254
-
255
- // Controlled
256
- <Dropdown
257
- options={options}
258
- value={selected}
259
- onChange={setSelected}
260
- placeholder="Select..."
261
- />
145
+ import { Card } from '@hotelcard/ui';
262
146
 
263
- // Uncontrolled with default
264
- <Dropdown
265
- options={options}
266
- defaultValue="1"
147
+ <Card
148
+ image="/hotel.jpg"
149
+ title="Grand Hotel Belvedere"
150
+ location="Lauterbrunnen"
151
+ stars={5}
152
+ price="From CHF 170"
153
+ originalPrice="CHF 340"
154
+ badge={{ text: '-50%', variant: 'primary' }}
155
+ isFavorite={false}
156
+ onFavoriteClick={() => toggleFavorite()}
157
+ onClick={() => navigateToHotel()}
267
158
  />
268
159
  ```
269
160
 
270
- **Props:**
271
- | Prop | Type | Default | Description |
272
- |------|------|---------|-------------|
273
- | `options` | `Array<{value, label}>` | `[]` | Dropdown options |
274
- | `value` | `string` | - | Controlled value |
275
- | `defaultValue` | `string` | - | Default value |
276
- | `onChange` | `(value: string) => void` | - | Change handler |
277
- | `placeholder` | `string` | `'Select...'` | Placeholder text |
278
- | `disabled` | `boolean` | `false` | Disable dropdown |
279
- | `error` | `boolean` | `false` | Show error state |
280
-
281
161
  ### Modal
282
162
 
283
163
  ```tsx
@@ -291,464 +171,105 @@ import { Modal } from '@hotelcard/ui';
291
171
  <h2>Modal Title</h2>
292
172
  <p>Modal content goes here.</p>
293
173
  </Modal>
294
-
295
- // Without close button
296
- <Modal
297
- isOpen={isOpen}
298
- onClose={handleClose}
299
- showCloseButton={false}
300
- >
301
- <CustomCloseButton />
302
- </Modal>
303
-
304
- // Disable backdrop click
305
- <Modal
306
- isOpen={isOpen}
307
- onClose={handleClose}
308
- disableBackdropClick
309
- >
310
- Must use close button
311
- </Modal>
312
174
  ```
313
175
 
314
- **Props:**
315
- | Prop | Type | Default | Description |
316
- |------|------|---------|-------------|
317
- | `isOpen` | `boolean` | - | Show/hide modal |
318
- | `onClose` | `() => void` | - | Close handler |
319
- | `children` | `ReactNode` | - | Modal content |
320
- | `width` | `string` | `'600px'` | Max width |
321
- | `showCloseButton` | `boolean` | `true` | Show X button |
322
- | `disableBackdropClick` | `boolean` | `false` | Prevent backdrop close |
323
-
324
- ### Chip
176
+ ### Checkbox & RadioButton
325
177
 
326
178
  ```tsx
327
- import { Chip } from '@hotelcard/ui';
328
-
329
- // Basic chip
330
- <Chip label="Filter" />
331
-
332
- // With count
333
- <Chip label="Hotels" count={42} />
334
-
335
- // Active state
336
- <Chip label="Selected" state="active" />
337
-
338
- // Non-removable
339
- <Chip label="Category" removable={false} onClick={() => {}} />
340
-
341
- // Sizes
342
- <Chip label="Medium" size="medium" />
343
- <Chip label="Small" size="small" />
344
- ```
345
-
346
- **Props:**
347
- | Prop | Type | Default | Description |
348
- |------|------|---------|-------------|
349
- | `label` | `string` | - | Chip text |
350
- | `count` | `number \| string` | - | Badge count |
351
- | `state` | `'idle' \| 'active' \| 'disabled'` | `'idle'` | State variant |
352
- | `size` | `'small' \| 'medium'` | `'small'` | Chip size |
353
- | `removable` | `boolean` | `true` | Show X button |
354
- | `onRemove` | `() => void` | - | Remove handler |
355
- | `onClick` | `() => void` | - | Click handler |
179
+ import { Checkbox, RadioButton } from '@hotelcard/ui';
356
180
 
357
- ### Divider
358
-
359
- ```tsx
360
- import { Divider } from '@hotelcard/ui';
361
-
362
- // Horizontal divider
363
- <Divider />
364
-
365
- // With label
366
- <Divider label="OR" />
367
-
368
- // Vertical divider
369
- <Divider orientation="vertical" />
370
- ```
371
-
372
- **Props:**
373
- | Prop | Type | Default | Description |
374
- |------|------|---------|-------------|
375
- | `label` | `string` | - | Center label text |
376
- | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Divider direction |
377
-
378
- ### SectionHeader
379
-
380
- ```tsx
381
- import { SectionHeader } from '@hotelcard/ui';
382
-
383
- // With "Show all" button
384
- <SectionHeader
385
- title="Latest Hotels"
386
- showAllLabel="Show all"
387
- onShowAllClick={() => navigate('/hotels')}
388
- />
389
-
390
- // Title only
391
- <SectionHeader title="Featured" />
392
- ```
393
-
394
- **Props:**
395
- | Prop | Type | Default | Description |
396
- |------|------|---------|-------------|
397
- | `title` | `string` | - | Section title |
398
- | `showAllLabel` | `string` | - | "Show all" button text |
399
- | `onShowAllClick` | `() => void` | - | Button click handler |
400
-
401
- ### CompactCard
402
-
403
- ```tsx
404
- import { CompactCard } from '@hotelcard/ui';
405
-
406
- <CompactCard
407
- image="/hotel.jpg"
408
- imageAlt="Hotel Grand"
409
- label="Hotel Grand Zurich"
410
- price="From CHF 120"
411
- stars={4}
412
- isSuperior
413
- badge={{ text: '-30%', variant: 'primary' }}
414
- onClick={() => navigate('/hotel/123')}
181
+ <Checkbox
182
+ label="Accept terms"
183
+ checked={accepted}
184
+ onChange={setAccepted}
415
185
  />
416
186
 
417
- // Swiss Lodge (stars = 6)
418
- <CompactCard
419
- label="Swiss Lodge Hotel"
420
- stars={6}
421
- swissLodgeLabel="Swiss Lodge"
187
+ <RadioButton
188
+ label="Option A"
189
+ checked={selected === 'a'}
190
+ onChange={() => setSelected('a')}
191
+ name="options"
422
192
  />
423
193
  ```
424
194
 
425
- **Props:**
426
- | Prop | Type | Default | Description |
427
- |------|------|---------|-------------|
428
- | `image` | `string` | - | Image URL |
429
- | `imageAlt` | `string` | `''` | Image alt text |
430
- | `label` | `string` | - | Hotel name |
431
- | `price` | `string` | - | Price text |
432
- | `stars` | `number` | - | Star rating (1-6) |
433
- | `isSuperior` | `boolean` | `false` | Superior indicator |
434
- | `badge` | `{text, variant}` | - | Badge config |
435
- | `onClick` | `() => void` | - | Click handler |
436
- | `swissLodgeLabel` | `string` | `'Swiss Lodge'` | Label for 6-star |
437
-
438
- ### Icons
439
-
440
- ```tsx
441
- import { HeartIcon, StarIcon, ChevronLeftIcon, ChevronRightIcon, PinIcon } from '@hotelcard/ui';
442
-
443
- <HeartIcon filled={false} size={24} />
444
- <StarIcon size={9} />
445
- <ChevronLeftIcon size={20} />
446
- <ChevronRightIcon size={20} />
447
- <PinIcon size={16} />
448
- ```
449
-
450
- ---
451
-
452
- ## CSS Variables (Theming)
453
-
454
- Components use CSS variables for theming. Your app should define these in a global CSS file:
455
-
456
- ```css
457
- :root {
458
- /* Colors */
459
- --background-action-primary-idle: #C81E4C;
460
- --background-action-primary-hover: #a3183d;
461
- --content-action-primary-inverse-idle: #ffffff;
462
- --content-general-primary: #1F2937;
463
- --content-general-secondary: #6B7280;
464
- --border-general-divider: #D1D5DB;
465
-
466
- /* Typography */
467
- --font-primary: system-ui, -apple-system, sans-serif;
468
- --font-weight-medium: 500;
469
- --font-weight-semibold: 600;
470
-
471
- /* Spacing & Sizing */
472
- --radius-lg: 12px;
473
- --radius-full: 9999px;
474
-
475
- /* ... see tokens.css for full list */
476
- }
477
- ```
478
-
479
- Components have fallback values, so they work without custom CSS variables.
480
-
481
195
  ---
482
196
 
483
197
  ## Types
484
198
 
485
- ```tsx
199
+ All components export their prop types:
200
+
201
+ ```typescript
486
202
  import type {
487
203
  ButtonProps,
488
204
  BadgeProps,
489
- RatingProps,
490
205
  InputProps,
206
+ CardProps,
207
+ CompactCardProps,
491
208
  CheckboxProps,
492
209
  RadioButtonProps,
493
210
  DropdownProps,
494
211
  ModalProps,
495
212
  ChipProps,
213
+ RatingProps,
496
214
  DividerProps,
497
215
  SectionHeaderProps,
498
- CompactCardProps,
499
216
  } from '@hotelcard/ui';
500
217
  ```
501
218
 
502
219
  ---
503
220
 
504
- ## Development Guide
505
-
506
- ### Project Structure
507
-
508
- ```
509
- packages/ui/
510
- ├── src/
511
- │ ├── components/
512
- │ │ ├── Button/
513
- │ │ │ ├── Button.tsx
514
- │ │ │ ├── Button.css # Prefixed classes (hc-btn-*)
515
- │ │ │ ├── Button.types.ts
516
- │ │ │ └── index.ts
517
- │ │ ├── Badge/
518
- │ │ ├── Rating/
519
- │ │ ├── Input/
520
- │ │ ├── Checkbox/
521
- │ │ ├── RadioButton/
522
- │ │ ├── Dropdown/
523
- │ │ ├── Modal/
524
- │ │ ├── Chip/
525
- │ │ ├── Divider/
526
- │ │ ├── SectionHeader/
527
- │ │ ├── CompactCard/
528
- │ │ └── icons/
529
- │ └── index.ts # Main exports
530
- ├── dist/ # Built output
531
- ├── package.json
532
- └── tsup.config.ts
533
- ```
534
-
535
- ### Adding a New Component
536
-
537
- 1. **Create component folder:**
538
- ```
539
- src/components/NewComponent/
540
- ├── NewComponent.tsx
541
- ├── NewComponent.css
542
- ├── NewComponent.types.ts
543
- └── index.ts
544
- ```
545
-
546
- 2. **Use prefixed CSS classes** (NOT CSS modules):
547
- ```tsx
548
- // NewComponent.tsx
549
- import './NewComponent.css';
550
-
551
- const cx = (className: string) => `hc-newcomp-${className}`;
552
-
553
- export const NewComponent = () => (
554
- <div className={cx('container')}>
555
- <span className={cx('text')}>Hello</span>
556
- </div>
557
- );
558
- ```
559
-
560
- 3. **Define CSS with prefixes:**
561
- ```css
562
- /* NewComponent.css */
563
- .hc-newcomp-container {
564
- display: flex;
565
- padding: var(--size-rem-1, 16px);
566
- }
567
-
568
- .hc-newcomp-text {
569
- color: var(--content-general-primary, #1F2937);
570
- }
571
- ```
572
-
573
- 4. **Export from index.ts:**
574
- ```tsx
575
- // src/index.ts
576
- export { NewComponent } from './components/NewComponent';
577
- export type { NewComponentProps } from './components/NewComponent';
578
- ```
579
-
580
- 5. **Build and test:**
581
- ```bash
582
- pnpm build
583
- ```
584
-
585
- ### CSS Class Naming Convention
586
-
587
- **Why prefixed classes instead of CSS Modules?**
588
-
589
- CSS Modules don't work well with tsup bundling - the class name mappings become empty objects. We use prefixed classes instead:
590
-
591
- - `hc-btn-*` for Button
592
- - `hc-badge-*` for Badge
593
- - `hc-input-*` for Input
594
- - `hc-checkbox-*` for Checkbox
595
- - `hc-radio-*` for RadioButton
596
- - `hc-dropdown-*` for Dropdown
597
- - `hc-modal-*` for Modal
598
- - `hc-chip-*` for Chip
599
- - `hc-divider-*` for Divider
600
- - `hc-section-*` for SectionHeader
601
- - `hc-compact-*` for CompactCard
602
-
603
- This prevents conflicts with consuming app's styles while ensuring styles are always applied.
604
-
605
- ### Building
221
+ ## Styling
606
222
 
607
- ```bash
608
- cd packages/ui
609
-
610
- # Build once
611
- pnpm build
223
+ Components use CSS variables for theming. Import the styles once in your app entry:
612
224
 
613
- # Watch mode for development
614
- pnpm dev
225
+ ```tsx
226
+ // main.tsx or App.tsx
227
+ import '@hotelcard/ui/dist/index.css';
615
228
  ```
616
229
 
617
- ### Testing Changes Locally
618
-
619
- 1. Build the package: `pnpm build`
620
- 2. In consuming app, reinstall: `npm install`
621
- 3. Restart dev server if needed
622
-
623
- ---
624
-
625
- ## Publishing to NPM
626
-
627
- ### Prerequisites
628
-
629
- 1. NPM account with access to `@hotelcard` organization
630
- 2. Logged in: `npm login`
230
+ ### Customizing with CSS Variables
631
231
 
632
- ### Publish Process
232
+ Override these variables in your app's CSS:
633
233
 
634
- ```bash
635
- cd packages/ui
234
+ ```css
235
+ :root {
236
+ /* Primary brand color */
237
+ --background-action-primary-idle: #C81E4C;
238
+ --background-action-primary-hover: #a3183d;
636
239
 
637
- # 1. Make sure everything builds
638
- pnpm build
240
+ /* Text colors */
241
+ --content-general-primary: #1F2937;
242
+ --content-general-secondary: #6B7280;
639
243
 
640
- # 2. Update version
641
- npm version patch # 0.0.2 -> 0.0.3
642
- # or
643
- npm version minor # 0.0.2 -> 0.1.0
644
- # or
645
- npm version major # 0.0.2 -> 1.0.0
244
+ /* Border colors */
245
+ --border-general-divider: #D1D5DB;
646
246
 
647
- # 3. Publish
648
- npm publish --access public
247
+ /* Typography */
248
+ --font-primary: system-ui, -apple-system, sans-serif;
649
249
 
650
- # 4. Commit version bump
651
- git add package.json
652
- git commit -m "chore(ui): release v0.0.3"
653
- git push
250
+ /* Border radius */
251
+ --radius-lg: 12px;
252
+ --radius-full: 9999px;
253
+ }
654
254
  ```
655
255
 
656
- ### Version Guidelines
657
-
658
- - `patch` (0.0.x): Bug fixes, style tweaks
659
- - `minor` (0.x.0): New components, new features
660
- - `major` (x.0.0): Breaking changes (API changes, removed props)
256
+ Components have built-in fallback values, so they work without custom CSS variables.
661
257
 
662
258
  ---
663
259
 
664
- ## Using in Apps
665
-
666
- ### hotelcard-app-v2 (Mobile App)
667
-
668
- ```tsx
669
- // src/main.tsx
670
- import '@hotelcard/ui/dist/index.css'; // REQUIRED
260
+ ## Requirements
671
261
 
672
- // src/library/index.ts (barrel export)
673
- export { Button, Badge, Input, Checkbox } from '@hotelcard/ui';
674
- export type { ButtonProps, BadgeProps, InputProps } from '@hotelcard/ui';
675
-
676
- // src/pages/SomePage.tsx
677
- import { Button, Input } from '@/library';
678
- ```
679
-
680
- ### hotelcard-ui (Website)
681
-
682
- ```tsx
683
- // src/main.tsx or app entry
684
- import '@hotelcard/ui/dist/index.css'; // REQUIRED
685
-
686
- // Use directly
687
- import { Button, Badge, Modal } from '@hotelcard/ui';
688
- ```
689
-
690
- ### Updating the Package
691
-
692
- ```bash
693
- # After a new version is published
694
- npm update @hotelcard/ui
695
-
696
- # Or for local development
697
- rm -rf node_modules/.cache
698
- npm install
699
- ```
262
+ - React 18+
263
+ - react-dom 18+
700
264
 
701
265
  ---
702
266
 
703
- ## Troubleshooting
704
-
705
- ### "Components render without styles"
706
-
707
- Make sure you imported the CSS:
708
- ```tsx
709
- import '@hotelcard/ui/dist/index.css';
710
- ```
711
-
712
- ### "Module not found" errors
713
-
714
- ```bash
715
- # Clear cache and reinstall
716
- rm -rf node_modules/.cache
717
- npm install
718
- ```
719
-
720
- ### "Types not found"
721
-
722
- Make sure you're importing types with `type`:
723
- ```tsx
724
- import type { ButtonProps } from '@hotelcard/ui';
725
- ```
726
-
727
- ### Local changes not reflecting
728
-
729
- ```bash
730
- # In @hotelcard/ui
731
- pnpm build
267
+ ## License
732
268
 
733
- # In consuming app
734
- npm install
735
- # Restart dev server
736
- ```
269
+ MIT
737
270
 
738
271
  ---
739
272
 
740
- ## Component Checklist
741
-
742
- When adding/modifying components:
743
-
744
- - [ ] Uses prefixed CSS classes (not CSS modules)
745
- - [ ] All CSS variables have fallback values
746
- - [ ] Props are typed in `.types.ts`
747
- - [ ] Exported from `src/index.ts`
748
- - [ ] Build passes: `pnpm build`
749
- - [ ] Tested in consuming app
750
- - [ ] README updated if new component
273
+ ## Maintained By
751
274
 
752
- ## License
753
-
754
- MIT
275
+ HotelCard AG - [hotelcard.ch](https://hotelcard.ch)