@stamcat/craftsman 0.0.27-alpha.15 → 0.0.27-alpha.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/AGENTS.md +8 -734
  2. package/Components.esm.js +7 -3
  3. package/Styles.esm.js +5 -5
  4. package/Utilities.esm.js +2 -2
  5. package/_virtual/_rolldown/runtime.esm.js +6 -3
  6. package/package.json +10 -1
  7. package/src/components/Button/Button.d.ts +1 -1
  8. package/src/components/Button/Button.esm.js +0 -1
  9. package/src/components/Carousel/Carousel.scss +1 -1
  10. package/src/components/DatePicker/DatePicker.css +1 -1
  11. package/src/components/DatePicker/DatePicker.scss +25 -117
  12. package/src/components/DatePicker/DatePicker2.esm.js +16 -11
  13. package/src/components/DatePicker/ReactCalendar.css +1 -1
  14. package/src/components/DatePicker/ReactCalendar.scss +9 -69
  15. package/src/components/DatePicker/_DateTimePicker.scss +67 -0
  16. package/src/components/DateTimePicker/DateTimePicker.css +1 -0
  17. package/src/components/DateTimePicker/DateTimePicker.d.ts +5 -0
  18. package/src/components/DateTimePicker/DateTimePicker.scss +86 -0
  19. package/src/components/DateTimePicker/DateTimePicker2.esm.js +78 -0
  20. package/src/components/Input/Input.scss +1 -1
  21. package/src/components/InputPhone/InputPhone.scss +1 -1
  22. package/src/components/Modal/Modal.esm.js +4 -4
  23. package/src/components/Modal/Modal.module.scss +1 -1
  24. package/src/components/Pagination/Pagination.scss +1 -1
  25. package/src/components/Text/Text.scss +1 -1
  26. package/src/components/TimePicker/TimePicker.css +1 -1
  27. package/src/components/TimePicker/TimePicker.d.ts +1 -1
  28. package/src/components/TimePicker/TimePicker.scss +102 -27
  29. package/src/components/TimePicker/TimePicker2.esm.js +61 -96
  30. package/src/components/TimePicker/TimePickerDisplay.d.ts +12 -18
  31. package/src/components/TimePicker/TimePickerDisplay.esm.js +54 -0
  32. package/src/components/Tooltip/Tooltip.css +1 -0
  33. package/src/components/Tooltip/Tooltip.scss +1 -1
  34. package/src/components/Tooltip/Tooltip2.esm.js +65 -0
  35. package/src/components/index.d.ts +2 -0
  36. package/src/stories/molecules/Carousel.scss +1 -1
  37. package/src/styles/global/components/_button.scss +11 -11
  38. package/src/styles/theme/components.esm.js +5 -6
  39. package/src/styles/utilities/_placeholders.scss +6 -0
  40. package/src/styles/utilities/layout.esm.js +3 -3
  41. package/src/utilities/types.d.ts +2 -2
  42. package/src/utilities/validations.d.ts +2 -0
  43. package/src/utilities/validations.esm.js +6 -2
package/AGENTS.md CHANGED
@@ -2,14 +2,18 @@
2
2
 
3
3
  This document explains how code-generation agents should use this library safely and correctly.
4
4
 
5
- ## What This Package Currently Exports
5
+ Full guidance has been split out for progressive loading:
6
6
 
7
- The package is currently built with component-level entry points only.
7
+ - **Global guidelines** (exports, hard rules, code block rendering, style utilities, theme authoring, `isEmpty`, code gen patterns, fallback strategy): see the [craftsman-component-usage skill](.github/skills/craftsman-component-usage/SKILL.md).
8
+ - **Component-specific guidelines** (props, behavior notes, examples): each component has its own `AGENTS.md` co-located with its source, for example [src/components/Button/AGENTS.md](src/components/Button/AGENTS.md).
9
+
10
+ ## Quick Reference
8
11
 
9
12
  Use these imports:
10
13
 
11
14
  ```tsx
12
15
  import { Button } from "@stamcat/craftsman/Button";
16
+ import { Carousel } from "@stamcat/craftsman/Carousel";
13
17
  import { Checkbox } from "@stamcat/craftsman/Checkbox";
14
18
  import { DatePicker } from "@stamcat/craftsman/DatePicker";
15
19
  import { Input } from "@stamcat/craftsman/Input";
@@ -17,10 +21,12 @@ import { InputPassword } from "@stamcat/craftsman/InputPassword";
17
21
  import { InputPhone } from "@stamcat/craftsman/InputPhone";
18
22
  import { Loader } from "@stamcat/craftsman/Loader";
19
23
  import { Modal } from "@stamcat/craftsman/Modal";
24
+ import { Pagination } from "@stamcat/craftsman/Pagination";
20
25
  import { RadioButton } from "@stamcat/craftsman/RadioButton";
21
26
  import { Select } from "@stamcat/craftsman/Select";
22
27
  import { Text } from "@stamcat/craftsman/Text";
23
28
  import { Textarea } from "@stamcat/craftsman/Textarea";
29
+ import { TimePicker } from "@stamcat/craftsman/TimePicker";
24
30
  ```
25
31
 
26
32
  Do not assume a root export like `@stamcat/craftsman` unless that export is explicitly added to package `exports`.
@@ -32,736 +38,4 @@ Do not assume a root export like `@stamcat/craftsman` unless that export is expl
32
38
  3. Do not import storybook files or internal style utilities from consuming applications.
33
39
  4. Prefer standard React props first; use custom props only when required.
34
40
 
35
- ## Code Block Rendering
36
-
37
- Craftsman's global styles automatically style `<code>` and `<code><pre>` elements. **Never create custom inline styles or wrapper divs to simulate a code block.** Use the native elements directly:
38
-
39
- ```tsx
40
- // Inline code — renders with pill/badge style
41
- <code>someValue</code>
42
-
43
- // Block code — renders with dark background, padding, and border-radius
44
- <code><pre>{`your
45
- multiline
46
- code here`}</pre></code>
47
- ```
48
-
49
- The two modes are driven by `_code.scss`:
50
- - `<code>` alone → light gray background, inline display
51
- - `<code>` containing `<pre>` → dark background (`--gray800`), block display, padded and rounded
52
-
53
- Do not create `preStyle`, `codeBlockStyle`, or equivalent inline style objects for this purpose. The global styles handle it.
54
-
55
- ## Style Utilities
56
-
57
- Craftsman provides TypeScript helpers and matching Sass functions for color, spacing, and breakpoints. Always use these instead of hard-coded values so theming and overrides work correctly.
58
-
59
- ### color()
60
-
61
- **TypeScript** — import from `@stamcat/craftsman/styles`:
62
-
63
- ```ts
64
- import { color, colors, hexToRgba } from "@stamcat/craftsman/styles";
65
-
66
- // Returns var(--blue500)
67
- color("blue500")
68
-
69
- // Returns rgb(from var(--blue500) r g b / 0.5)
70
- color("blue500", "rgba", 0.5)
71
-
72
- // Fallback for environments that don't support CSS relative color syntax
73
- hexToRgba(colors.blue500, 0.5)
74
- ```
75
-
76
- **Sass** — import `functions` as `u`:
77
-
78
- ```scss
79
- @use "@stamcat/craftsman/styles/utilities/functions" as u;
80
-
81
- .element {
82
- color: #{u.color(blue500)};
83
- background: #{u.color(black, rgba, 0.4)};
84
- }
85
- ```
86
-
87
- - `color(name)` → `var(--name)` — always prefer this over hard-coded hex values so theme overrides apply.
88
- - `color(name, rgba, alpha)` → `rgb(from var(--name) r g b / alpha)` — uses CSS relative color syntax; verify browser support for your target.
89
- - Never hard-code hex color values. Always use `color()` or a CSS variable.
90
-
91
- ### width()
92
-
93
- **TypeScript** — import from `@stamcat/craftsman/styles`:
94
-
95
- ```ts
96
- import { width } from "@stamcat/craftsman/styles";
97
-
98
- width("gutter") // var(--w-gutter, 16px)
99
- width("gutter", 0.5) // calc(var(--w-gutter, 16px) * 0.5)
100
- width("column", 3) // calc((var(--w-column) * 3) + (var(--w-gutter) * 2))
101
- ```
102
-
103
- **Sass** — import `functions` as `u`:
104
-
105
- ```scss
106
- @use "@stamcat/craftsman/styles/utilities/functions" as u;
107
-
108
- .card {
109
- padding: #{u.width(gutter)};
110
- gap: #{u.width(gutter, 0.5)};
111
- max-width: #{u.width(column, 4)};
112
- }
113
- ```
114
-
115
- Valid keys: `text` · `gutter` · `column` · `tablet` · `desktop` · `extDesktop` · `mobileMax` · `tabletMax` · `desktopMax`
116
-
117
- - The `column` key automatically accounts for gutters between columns.
118
- - Never use hard-coded `px` values for spacing or layout widths. Always use `width()`.
119
-
120
- ### breakpoint()
121
-
122
- **TypeScript** — returns a full `@media` rule string for CSS-in-JS:
123
-
124
- ```ts
125
- import { breakpoint, media } from "@stamcat/craftsman/styles";
126
-
127
- // Full rule — use inside styled-components, emotion, or style injection
128
- breakpoint("desktop", "font-size: 18px;")
129
- // => "@media (min-width: 1040px) { font-size: 18px; }"
130
-
131
- // Raw query string only — use for conditional logic or matchMedia
132
- media.tablet // => "(min-width: 660px)"
133
- ```
134
-
135
- **Sass** — `@include breakpoint(key)` wraps content in the correct `@media` query:
136
-
137
- ```scss
138
- @use "@stamcat/craftsman/styles/utilities/functions" as u;
139
-
140
- .sidebar {
141
- display: none;
142
-
143
- @include u.breakpoint(tablet) {
144
- display: block;
145
- }
146
- }
147
- ```
148
-
149
- Available breakpoint keys: `tablet` · `tabletMax` · `tabletOnly` · `desktop` · `desktopMax` · `desktopOnly` · `extDesktop` · `mobileMax` · `mobileOnly` · `mobileTablet`
150
-
151
- - Breakpoint values come from `_config.scss` and stay in sync with any project overrides.
152
- - Never hard-code `@media (min-width: 1040px)` or similar — always use `breakpoint()` so values stay consistent with config.
153
-
154
- ## Component Contracts
155
-
156
- ### Button
157
-
158
- Import:
159
-
160
- ```tsx
161
- import { Button } from "@stamcat/craftsman/Button";
162
- ```
163
-
164
- Props:
165
-
166
- - Inherits all native `<button>` props.
167
- - `variant?: "primary" | "default" | "text"` (default: `"default"`)
168
- - `size?: number` (default visual scale is `1`)
169
- - `styles?: React.CSSProperties` (inline style override)
170
-
171
- Behavior notes:
172
-
173
- - `type` defaults to `"button"`.
174
- - For `variant !== "default"`, variant is appended to `className` (for example `"primary"`).
175
- - `className` is preserved and merged after component classes.
176
- - Theme component overrides are selector-based CSS emitted by `ThemeProvider`; `theme.components.*` accepts JS style objects or raw CSS/Sass strings for the target selector.
177
- - If `children` is empty (per `isEmpty`), the component renders nothing.
178
- - `size` is clamped to `[0.1, 10]` before styling is applied.
179
- - When `size` is provided, Button scales:
180
- - `border-radius: calc(var(--btn-border-radius) * size)`
181
- - `padding: calc(var(--btn-pad-y) * size) calc(var(--btn-pad-x) * size)`
182
- - `font-size: max(10px, calc(var(--w-text) * size))`
183
- - There is no dedicated icon/loading prop. Icons, loaders, and mixed content are passed as `children`.
184
- - Supports both real disable (`disabled={true}`) and style-only disable (`className="disabled"`).
185
- - Native button modes are supported (`type="button" | "submit" | "reset"`).
186
- - Accessibility props such as `aria-label` pass through unchanged.
187
-
188
- Example:
189
-
190
- ```tsx
191
- <Button variant="primary" onClick={onSave}>Save</Button>
192
- ```
193
-
194
- Story-aligned usage examples:
195
-
196
- ```tsx
197
- // Scaled compact button (size is clamped to [0.1, 10])
198
- <Button variant="primary" size={0.5}>Mini Action</Button>
199
-
200
- // Icon content
201
- <Button variant="primary">
202
- <TruckIcon size={20} /> <span>Ship</span>
203
- </Button>
204
-
205
- // Loading content
206
- <Button aria-label="Saving">
207
- <Loader type="boxy" width={32} color="#de13ca" />
208
- </Button>
209
-
210
- // Style-only disabled appearance while still allowing click handlers
211
- <Button className="disabled" onClick={onClick}>Disabled Look</Button>
212
-
213
- // Native submit behavior
214
- <Button type="submit" aria-label="Save form">Save</Button>
215
- ```
216
-
217
- Implementation caution:
218
-
219
- - Size scaling depends on CSS variables provided by the package global styles (`--btn-border-radius`, `--btn-pad-y`, `--btn-pad-x`, `--w-text`).
220
-
221
- ### Input
222
-
223
- Import:
224
-
225
- ```tsx
226
- import { Input } from "@stamcat/craftsman/Input";
227
- ```
228
-
229
- Props:
230
-
231
- - Inherits native `<input>` props.
232
- - `label?: string | ReactNode`
233
- - `labelPosition?: "top" | "left" | "bottom" | "right" | "inside" | "hidden"` (default: `"top"`)
234
- - `error?: string | boolean | ReactNode`
235
- - `required?: boolean`
236
- - `styles?: React.CSSProperties` (wrapper override)
237
- - `type?: TextInputType` (checkbox/radio excluded)
238
-
239
- Behavior notes:
240
-
241
- - `id` is preserved; if omitted, a stable React `useId` value is used.
242
- - Floating-label behavior is supported when `labelPosition="inside"`.
243
- - `endAdornment` exists as an internal extension point used by `InputPassword`; consumer apps should prefer `InputPassword` rather than wiring password toggles on `Input`.
244
-
245
- Example:
246
-
247
- ```tsx
248
- <Input type="email" placeholder="you@company.com" required />
249
- ```
250
-
251
- ### InputPassword
252
-
253
- Import:
254
-
255
- ```tsx
256
- import { InputPassword } from "@stamcat/craftsman/InputPassword";
257
- ```
258
-
259
- Props:
260
-
261
- - Extends `Input` props, with `type` constrained to password mode.
262
- - Includes all label, error, required, and wrapper style props from `Input`.
263
-
264
- Behavior notes:
265
-
266
- - Built on top of `Input` and adds an internal password visibility state.
267
- - Renders an in-field show/hide toggle button.
268
- - Toggle is accessible: real button element, keyboard operable, and updates `aria-label` + `aria-pressed`.
269
- - Use this component for password fields instead of `Input type="password"`.
270
-
271
- Example:
272
-
273
- ```tsx
274
- <InputPassword
275
- label="Password"
276
- placeholder="Enter your password"
277
- autoComplete="current-password"
278
- />
279
- ```
280
-
281
- ### Checkbox
282
-
283
- Import:
284
-
285
- ```tsx
286
- import { Checkbox } from "@stamcat/craftsman/Checkbox";
287
- ```
288
-
289
- Props:
290
-
291
- - Extends `Input` props.
292
- - `type?: "checkbox"` (default: `"checkbox"`)
293
- - `labelPosition?: "left" | "right" | "top" | "bottom"` (default: `"right"`)
294
-
295
- Behavior notes:
296
-
297
- - `Checkbox` is a thin wrapper around `Input` with checkbox-specific guardrails.
298
- - The wrapper enforces checkbox type by default and narrows label position options to common checkbox layouts.
299
- - Controlled and uncontrolled patterns are both supported (`checked` + `onChange`, or `defaultChecked`).
300
-
301
- Example:
302
-
303
- ```tsx
304
- <Checkbox
305
- id="accept-terms"
306
- name="terms"
307
- value="accepted"
308
- label="Accept terms"
309
- checked={accepted}
310
- onChange={(event) => setAccepted(event.currentTarget.checked)}
311
- />
312
- ```
313
-
314
- ### RadioButton
315
-
316
- Import:
317
-
318
- ```tsx
319
- import { RadioButton } from "@stamcat/craftsman/RadioButton";
320
- ```
321
-
322
- Props:
323
-
324
- - Extends `Input` props.
325
- - `type?: "radio"` (default: `"radio"`)
326
- - `labelPosition?: "left" | "right"` (default: `"right"`)
327
-
328
- Behavior notes:
329
-
330
- - `RadioButton` is a thin wrapper around `Input` with radio-specific guardrails.
331
- - Group behavior is native HTML radio behavior: use a shared `name` across options.
332
- - Controlled and uncontrolled patterns are both supported (`checked` + `onChange`, or `defaultChecked`).
333
-
334
- Example:
335
-
336
- ```tsx
337
- <>
338
- <RadioButton
339
- id="captain-kirk"
340
- name="favorite-captain"
341
- value="Kirk"
342
- label="James T. Kirk"
343
- checked={selected === "Kirk"}
344
- onChange={(event) => setSelected(event.currentTarget.value)}
345
- />
346
- <RadioButton
347
- id="captain-picard"
348
- name="favorite-captain"
349
- value="Picard"
350
- label="Jean-Luc Picard"
351
- checked={selected === "Picard"}
352
- onChange={(event) => setSelected(event.currentTarget.value)}
353
- />
354
- </>
355
- ```
356
-
357
- ### Modal
358
-
359
- Import:
360
-
361
- ```tsx
362
- import { Modal } from "@stamcat/craftsman/Modal";
363
- ```
364
-
365
- Props:
366
-
367
- - Inherits all native `<div>` props.
368
- - `visible?: boolean` (when falsy, component renders nothing)
369
- - `onDismiss?: () => void`
370
- - `type?: "dialog" | "panel"` (default: `"dialog"`)
371
- - `header?: string | React.ReactNode`
372
- - `backgroundDismiss?: boolean` (default behavior: `true`)
373
- - `hideDismissIcon?: boolean` (default behavior: close icon is shown)
374
- - `footer?: React.ReactNode`
375
- - `styles?: React.CSSProperties` (applies to outer modal wrapper)
376
-
377
- Behavior notes:
378
-
379
- - Modal is controlled; parent owns open/close state via `visible` and `onDismiss`.
380
- - Close icon and background click both dismiss through `onDismiss`.
381
- - Background dismiss only runs when `backgroundDismiss` is `true` or `undefined`.
382
- - Dismiss uses a short close animation before calling `onDismiss` (~280ms timeout).
383
- - `type="dialog"` renders centered responsive dialog sizing; `type="panel"` renders a right-side panel.
384
- - `footer` is rendered in an action row container below modal content.
385
-
386
- Example:
387
-
388
- ```tsx
389
- const [open, setOpen] = useState(false);
390
-
391
- <>
392
- <Button variant="primary" onClick={() => setOpen(true)}>Open</Button>
393
- <Modal
394
- visible={open}
395
- onDismiss={() => setOpen(false)}
396
- type="dialog"
397
- header="Confirm Action"
398
- footer={<><Button onClick={() => setOpen(false)}>Cancel</Button><Button variant="primary">Confirm</Button></>}
399
- >
400
- <p>Are you sure?</p>
401
- </Modal>
402
- </>
403
- ```
404
-
405
- ### Loader
406
-
407
- Import:
408
-
409
- ```tsx
410
- import { Loader } from "@stamcat/craftsman/Loader";
411
- ```
412
-
413
- Props:
414
-
415
- - Inherits all native `<div>` props.
416
- - `type` is required and must be one of:
417
- - `"dots"`
418
- - `"dots-trace"`
419
- - `"dots-bounce"`
420
- - `"dots-orbit"`
421
- - `"dashes"`
422
- - `"spinner"`
423
- - `"swirl"`
424
- - `"ball"`
425
- - `"boxy"`
426
- - `"factory"`
427
- - `color?: string` (default: `"black"`)
428
- - `width?: number` (optional, variant-dependent default behavior)
429
- - `styles?: React.CSSProperties` (inline style override)
430
-
431
- Example:
432
-
433
- ```tsx
434
- <Loader type="spinner" color="var(--blue500)" width={40} aria-label="Loading" />
435
- ```
436
-
437
- ### Textarea
438
-
439
- Import:
440
-
441
- ```tsx
442
- import { Textarea } from "@stamcat/craftsman/Textarea";
443
- ```
444
-
445
- Props:
446
-
447
- - Inherits all native `<textarea>` props.
448
- - `label?: string | ReactNode`
449
- - `labelPosition?: "top" | "left" | "bottom" | "right" | "inside" | "hidden"` (default: `"top"`)
450
- - `error?: string | boolean | ReactNode`
451
- - `required?: boolean`
452
- - `rows?: number`
453
-
454
- Behavior notes:
455
-
456
- - Shares the same `InputWrapper` as `Input` — label, error, and required behavior is identical.
457
- - `id` is auto-generated via `useId` if not provided.
458
-
459
- Example:
460
-
461
- ```tsx
462
- <Textarea
463
- label="Your Message"
464
- placeholder="Type here"
465
- rows={4}
466
- required
467
- />
468
- ```
469
-
470
- ### Select
471
-
472
- Import:
473
-
474
- ```tsx
475
- import { Select } from "@stamcat/craftsman/Select";
476
- ```
477
-
478
- Props:
479
-
480
- - Inherits all native `<select>` props.
481
- - `label?: string | ReactNode`
482
- - `labelPosition?: "top" | "left" | "bottom" | "right" | "inside" | "hidden"` (default: `"top"`)
483
- - `error?: string | boolean | ReactNode`
484
- - `required?: boolean`
485
- - `options?: Array<{ label: string; value: string }>`
486
-
487
- Behavior notes:
488
-
489
- - Built on the native `<select>` element via `InputWrapper`.
490
- - Pass options as a plain array — do not render `<option>` children manually.
491
- - `id` is auto-generated via `useId` if not provided.
492
-
493
- Example:
494
-
495
- ```tsx
496
- <Select
497
- label="Favorite Fruit"
498
- required
499
- options={[
500
- { value: "", label: "Select one..." },
501
- { value: "apple", label: "Apple" },
502
- { value: "banana", label: "Banana" },
503
- ]}
504
- />
505
- ```
506
-
507
- ### InputPhone
508
-
509
- Import:
510
-
511
- ```tsx
512
- import { InputPhone } from "@stamcat/craftsman/InputPhone";
513
- ```
514
-
515
- Props:
516
-
517
- - Extends `PhoneInputProps` from `react-international-phone`.
518
- - `label?: string | ReactNode`
519
- - `labelPosition?: "top" | "left" | "bottom" | "right" | "inside" | "hidden"` (default: `"top"`)
520
- - `error?: string | boolean | ReactNode`
521
- - `required?: boolean`
522
- - `defaultCountry?: string` (default: `"us"`)
523
- - `preferredCountries?: string[]`
524
- - `endAdornment?: ReactNode`
525
-
526
- Behavior notes:
527
-
528
- - Powered by `react-international-phone` for i18n-aware phone number formatting.
529
- - Country selector dropdown opens below the field; ensure the container has at least 350px of vertical space.
530
- - Use `preferredCountries` to surface commonly used countries at the top of the dropdown.
531
-
532
- Example:
533
-
534
- ```tsx
535
- <InputPhone
536
- label="Phone Number"
537
- defaultCountry="us"
538
- preferredCountries={["us", "gb", "ca"]}
539
- required
540
- />
541
- ```
542
-
543
- ### DatePicker
544
-
545
- Import:
546
-
547
- ```tsx
548
- import { DatePicker } from "@stamcat/craftsman/DatePicker";
549
- ```
550
-
551
- Props:
552
-
553
- - Extends `DatePickerProps` from `react-date-picker`.
554
- - `label?: string | ReactNode`
555
- - `labelPosition?: "top" | "left" | "bottom" | "right" | "inside" | "hidden"` (default: `"top"`)
556
- - `error?: string | boolean | ReactNode`
557
- - `required?: boolean`
558
- - `value?: Date | null`
559
- - `onChange?: (value: DatePickerProps["value"]) => void`
560
-
561
- Behavior notes:
562
-
563
- - Controlled component — parent owns `value` and `onChange`.
564
- - Calendar flyout renders below the input; ensure the container has at least 400px of vertical space.
565
- - Use `useState` to manage the selected date value.
566
-
567
- Example:
568
-
569
- ```tsx
570
- const [date, setDate] = useState<Date | null>(null);
571
-
572
- <DatePicker
573
- label="Appointment Date"
574
- value={date}
575
- onChange={setDate}
576
- required
577
- />
578
- ```
579
-
580
- ### Text
581
-
582
- Import:
583
-
584
- ```tsx
585
- import { Text } from "@stamcat/craftsman/Text";
586
- ```
587
-
588
- Props:
589
-
590
- - Inherits all native HTML element props.
591
- - `as?: TextTags` — HTML tag to render (default: `"div"`)
592
- - `richText?: boolean` — when `true`, renders sanitized HTML from a string `children` value (always renders as `<div>`)
593
- - `type?: TextType` — overrides base type styling (e.g. `"display"`, `"heading"`)
594
- - `size?: TextSize` — overrides base size styling
595
- - `alignment?: "center" | "left" | "right"` — legacy text-align shorthand
596
-
597
- Behavior notes:
598
-
599
- - Uses global HTML5 tag declarations by default; `type` and `size` override base styling.
600
- - `richText` mode sanitizes HTML via DOMPurify. Always pass a string as `children` in this mode.
601
- - Prefer semantic HTML5 tags via `as` over using `type`/`size` overrides.
602
-
603
- Example:
604
-
605
- ```tsx
606
- // Semantic heading
607
- <Text as="h2">Section Title</Text>
608
-
609
- // Sanitized rich text from a CMS
610
- <Text richText>{'<p><strong>Hello</strong> world</p>'}</Text>
611
- ```
612
-
613
- ### Toast (react-toastify)
614
-
615
- Craftsman re-exports `toast` and `ToastContainer` from `react-toastify`. No custom wrapper is needed.
616
-
617
- Import:
618
-
619
- ```tsx
620
- import { toast, ToastContainer } from "react-toastify";
621
- import "react-toastify/dist/ReactToastify.css";
622
- ```
623
-
624
- Usage:
625
-
626
- - Render one `<ToastContainer>` near the root of your app.
627
- - Call `toast(...)` anywhere in response to user actions.
628
- - `ToastContainer` props: `position`, `autoClose`, `theme` (`"light" | "dark" | "colored"`), `closeOnClick`, `pauseOnHover`, `draggable`, `newestOnTop`.
629
-
630
- Example:
631
-
632
- ```tsx
633
- // Root layout
634
- <ToastContainer position="bottom-right" autoClose={3000} theme="light" />
635
-
636
- // Anywhere in the app
637
- toast("Saved successfully!");
638
- toast.error("Something went wrong.");
639
- toast.success("Profile updated.");
640
- toast.warning("Unsaved changes.");
641
- toast.info("New version available.");
642
- ```
643
-
644
- ## Styling Expectations
645
-
646
- - Components are built with SCSS modules and class-based variant hooks.
647
- - If your app does not include this package's global CSS variable setup, visual output may differ.
648
- - Agents should avoid hard-coding assumptions about token names beyond what the consumer app already defines.
649
-
650
- ## Theme Authoring
651
-
652
- - `theme.root` supports JS style objects and raw CSS/Sass strings.
653
- - `theme.components.*` supports JS style objects and raw CSS/Sass strings.
654
- - `theme.widths` accepts a partial record of width/breakpoint keys to override the default `--w-*` CSS variables. Values are numbers in `px`.
655
- - String component styles are applied to the mapped target selector (for example `button`, `input[type='checkbox']`).
656
- - For multi-file Sass workflows with syntax highlighting and mixins, import compiled CSS text via `*.scss?inline`.
657
-
658
- Valid `theme.widths` keys: `"text" | "gutter" | "column" | "tablet" | "desktop" | "extDesktop" | "mobileMax" | "tabletMax" | "desktopMax"`
659
-
660
- Example:
661
-
662
- ```tsx
663
- import greenRoot from "./green.root.scss?inline";
664
- import greenButton from "./green.button.scss?inline";
665
-
666
- export const theme = {
667
- widths: {
668
- gutter: 20,
669
- tablet: 768,
670
- },
671
- root: greenRoot,
672
- components: {
673
- button: greenButton,
674
- },
675
- };
676
- ```
677
-
678
- ## Utility Functions
679
-
680
- ### `isEmpty`
681
-
682
- The package exports an `isEmpty` utility. **Always use it instead of writing inline empty checks.**
683
-
684
- Import:
685
-
686
- ```ts
687
- import { isEmpty } from "@stamcat/craftsman/utilities/validations";
688
- ```
689
-
690
- It returns `true` for:
691
-
692
- - `undefined`
693
- - `null`
694
- - empty objects — `{}`
695
- - strings that are empty or whitespace-only — `""`, `" "`
696
- - arrays with no elements — `[]`
697
-
698
- Examples:
699
-
700
- ```ts
701
- // DO — use isEmpty
702
- if (isEmpty(value)) { ... }
703
- if (!isEmpty(items)) { ... }
704
-
705
- // DO NOT — write these manually
706
- if (value === undefined || value === null) { ... }
707
- if (typeof value === "string" && value.trim().length === 0) { ... }
708
- if (Object.keys(obj).length === 0) { ... }
709
- if (arr.length === 0) { ... }
710
- ```
711
-
712
- ### Style utilities parity (`color`, `width`, `breakpoint`)
713
-
714
- These utility patterns exist in both TypeScript and Sass.
715
-
716
- TypeScript usage:
717
-
718
- ```ts
719
- import { color, width, breakpoint } from "@stamcat/craftsman/styles";
720
-
721
- const accent = color("blue500");
722
- const alphaAccent = color("blue500", "rgba", 0.32);
723
- const twoColumns = width("column", 2);
724
- const mobileRule = breakpoint("mobileMax", "h4{font-size:14px;}");
725
- ```
726
-
727
- Sass usage (framework source):
728
-
729
- ```scss
730
- @use "./src/styles/utilities" as u;
731
-
732
- .example {
733
- color: #{u.color(blue500)};
734
- background: #{u.color(blue500, rgba, 0.32)};
735
- max-width: #{u.width(column, 2)};
736
- }
737
-
738
- @include u.breakpoint(mobileMax) {
739
- .example { font-size: #{u.width(text)}; }
740
- }
741
- ```
742
-
743
- ## Code Generation Patterns to Prefer
744
-
745
- 1. Generate fully typed React usage examples.
746
- 2. Keep accessibility props in place (`aria-label`, `disabled`, semantic `type`).
747
- 3. Use `variant="primary"` for main actions and `variant="text"` for low-emphasis actions.
748
- 4. For loading states, pair `Loader` with accessible status text where needed.
749
- 5. **Functional component declaration order** — always organize the body in this sequence:
750
- 1. Hook calls (`useSomething`)
751
- 2. State (`useState`)
752
- 3. Derived state / variables (values computed from state or props)
753
- 4. Action handlers (`const handle*`, `const dispatch*`, `useEffect`)
754
-
755
- ## Known Limitations (Current Package State)
756
-
757
- 1. README is minimal; treat this guide as the source of truth for agent usage.
758
- 2. Theme utilities exist in source but are not guaranteed public package exports.
759
- 3. `Progress` exists in source but is incomplete and intentionally omitted from this guide for now.
760
-
761
- ## Safe Fallback Strategy for Agents
762
-
763
- If uncertain about available exports:
764
41
 
765
- 1. Use only `Button`, `Checkbox`, `DatePicker`, `Input`, `InputPassword`, `InputPhone`, `Loader`, `Modal`, `RadioButton`, `Select`, `Text`, and `Textarea` from their component entry points.
766
- 2. Do not invent package APIs.
767
- 3. Prefer native HTML elements for anything not explicitly exported.