@stamcat/craftsman 0.0.31-beta.3 → 0.0.31-beta.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.
package/AGENTS.md
CHANGED
|
@@ -4,9 +4,9 @@ This document explains how code-generation agents should use this library safely
|
|
|
4
4
|
|
|
5
5
|
Full guidance has been split out for progressive loading:
|
|
6
6
|
|
|
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](
|
|
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](skills/craftsman-component-usage/SKILL.md).
|
|
8
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
|
-
- **Device/browser detection** (prefer `react-device-detect` over custom user-agent/viewport checks): see the [craftsman-device-detection skill](
|
|
9
|
+
- **Device/browser detection** (prefer `react-device-detect` over custom user-agent/viewport checks): see the [craftsman-device-detection skill](skills/craftsman-device-detection/SKILL.md).
|
|
10
10
|
|
|
11
11
|
## Quick Reference
|
|
12
12
|
|
|
@@ -41,9 +41,9 @@ Do not assume a root export like `@stamcat/craftsman` unless that export is expl
|
|
|
41
41
|
|
|
42
42
|
## Hard Rules for Agents
|
|
43
43
|
|
|
44
|
-
1. Never deep-import from package internals (for example `@stamcat/craftsman/src/...`).
|
|
44
|
+
1. Never deep-import from package internals (for example `@stamcat/craftsman/src/...`). This applies to Sass too — `@use "@stamcat/craftsman/src/styles/utilities/functions"` will fail to resolve; use the published subpath `@stamcat/craftsman/styles/utilities/functions` instead.
|
|
45
45
|
2. Only use documented component entry points.
|
|
46
|
-
3. Do not import storybook files
|
|
46
|
+
3. Do not import storybook files from consuming applications. Style utilities are fine to import, but only via their published subpaths (`@stamcat/craftsman/styles/utilities/functions`, `/mixins`, `/placeholders`) — never via a `/src/...` path.
|
|
47
47
|
4. Prefer standard React props first; use custom props only when required.
|
|
48
48
|
|
|
49
49
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamcat/craftsman",
|
|
3
|
-
"version": "0.0.31-beta.
|
|
3
|
+
"version": "0.0.31-beta.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A powerful, lightweight framework for design systems",
|
|
6
6
|
"repository": {
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
"bugs": {
|
|
17
17
|
"url": "https://github.com/Stamcat/craftsman/issues"
|
|
18
18
|
},
|
|
19
|
+
"x-agent-instructions": "./AGENTS.md",
|
|
20
|
+
"x-agent-components": "./src/components/*/AGENTS.md",
|
|
19
21
|
"types": "./src/components/index.d.ts",
|
|
20
22
|
"main": "./Components.esm.js",
|
|
21
23
|
"homepage": "https://github.com/Stamcat/craftsman#readme",
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craftsman-component-usage
|
|
3
|
+
description: 'Global AI agent guidelines for using the @stamcat/craftsman component library — package exports, hard import rules, code block rendering, Toast usage, and code generation patterns. Use when generating or reviewing code that imports from @stamcat/craftsman or needing per-component prop contracts (Button, Input, Modal, DatePicker, TimePicker, Carousel, Pagination, etc). See the craftsman-style-utilities, craftsman-utility-functions, and craftsman-device-detection skills for styling, utility function, and device detection guidance.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Craftsman Component Library Usage
|
|
7
|
+
|
|
8
|
+
Global rules for AI agents generating code against `@stamcat/craftsman`. For a specific component's props, behavior notes, and examples, open that component's `AGENTS.md` co-located with its source — see the index below. For styling utilities and theme authoring, see the [craftsman-style-utilities skill](../craftsman-style-utilities/SKILL.md). For the `isEmpty` utility and style utility parity notes, see the [craftsman-utility-functions skill](../craftsman-utility-functions/SKILL.md). For device/browser/OS detection, see the [craftsman-device-detection skill](../craftsman-device-detection/SKILL.md).
|
|
9
|
+
|
|
10
|
+
## What This Package Currently Exports
|
|
11
|
+
|
|
12
|
+
The package is built with component-level entry points only.
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Button } from "@stamcat/craftsman/Button";
|
|
16
|
+
import { Carousel } from "@stamcat/craftsman/Carousel";
|
|
17
|
+
import { Checkbox } from "@stamcat/craftsman/Checkbox";
|
|
18
|
+
import { DatePicker } from "@stamcat/craftsman/DatePicker";
|
|
19
|
+
import { DateRangePicker } from "@stamcat/craftsman/DateRangePicker";
|
|
20
|
+
import { Input } from "@stamcat/craftsman/Input";
|
|
21
|
+
import { InputNumber } from "@stamcat/craftsman/InputNumber";
|
|
22
|
+
import { InputPassword } from "@stamcat/craftsman/InputPassword";
|
|
23
|
+
import { InputPhone } from "@stamcat/craftsman/InputPhone";
|
|
24
|
+
import { Loader } from "@stamcat/craftsman/Loader";
|
|
25
|
+
import { Modal } from "@stamcat/craftsman/Modal";
|
|
26
|
+
import { Pagination } from "@stamcat/craftsman/Pagination";
|
|
27
|
+
import { RadioButton } from "@stamcat/craftsman/RadioButton";
|
|
28
|
+
import { Select } from "@stamcat/craftsman/Select";
|
|
29
|
+
import { Text } from "@stamcat/craftsman/Text";
|
|
30
|
+
import { Textarea } from "@stamcat/craftsman/Textarea";
|
|
31
|
+
import { TimePicker } from "@stamcat/craftsman/TimePicker";
|
|
32
|
+
import { DateTimePicker } from "@stamcat/craftsman/DateTimePicker";
|
|
33
|
+
import { Toggle } from "@stamcat/craftsman/Toggle";
|
|
34
|
+
import { Tooltip } from "@stamcat/craftsman/Tooltip";
|
|
35
|
+
import { Notice } from "@stamcat/craftsman/Notice";
|
|
36
|
+
import { SortableList } from "@stamcat/craftsman/SortableList";
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Do not assume a root export like `@stamcat/craftsman` unless that export is explicitly added to package `exports`.
|
|
40
|
+
|
|
41
|
+
## Hard Rules for Agents
|
|
42
|
+
|
|
43
|
+
1. Never deep-import from package internals (for example `@stamcat/craftsman/src/...`). This applies to Sass too — `@use "@stamcat/craftsman/src/styles/utilities/functions"` will fail to resolve; use the published subpath `@stamcat/craftsman/styles/utilities/functions` instead.
|
|
44
|
+
2. Only use documented component entry points.
|
|
45
|
+
3. Do not import storybook files from consuming applications. Style utilities are fine to import, but only via their published subpaths (`@stamcat/craftsman/styles/utilities/functions`, `/mixins`, `/placeholders`) — never via a `/src/...` path.
|
|
46
|
+
4. Prefer standard React props first; use custom props only when required.
|
|
47
|
+
|
|
48
|
+
## Component Index
|
|
49
|
+
|
|
50
|
+
Each entry links to the `AGENTS.md` co-located with that component's source for props, behavior notes, and examples.
|
|
51
|
+
|
|
52
|
+
| Component | Docs |
|
|
53
|
+
|---|---|
|
|
54
|
+
| Button | [../../src/components/Button/AGENTS.md](../../src/components/Button/AGENTS.md) |
|
|
55
|
+
| Input | [../../src/components/Input/AGENTS.md](../../src/components/Input/AGENTS.md) |
|
|
56
|
+
| InputPassword | [../../src/components/InputPassword/AGENTS.md](../../src/components/InputPassword/AGENTS.md) |
|
|
57
|
+
| Checkbox | [../../src/components/Checkbox/AGENTS.md](../../src/components/Checkbox/AGENTS.md) |
|
|
58
|
+
| RadioButton | [../../src/components/RadioButton/AGENTS.md](../../src/components/RadioButton/AGENTS.md) |
|
|
59
|
+
| Modal | [../../src/components/Modal/AGENTS.md](../../src/components/Modal/AGENTS.md) |
|
|
60
|
+
| Pagination | [../../src/components/Pagination/AGENTS.md](../../src/components/Pagination/AGENTS.md) |
|
|
61
|
+
| Carousel | [../../src/components/Carousel/AGENTS.md](../../src/components/Carousel/AGENTS.md) |
|
|
62
|
+
| Loader | [../../src/components/Loader/AGENTS.md](../../src/components/Loader/AGENTS.md) |
|
|
63
|
+
| Textarea | [../../src/components/Textarea/AGENTS.md](../../src/components/Textarea/AGENTS.md) |
|
|
64
|
+
| Select | [../../src/components/Select/AGENTS.md](../../src/components/Select/AGENTS.md) |
|
|
65
|
+
| InputPhone | [../../src/components/InputPhone/AGENTS.md](../../src/components/InputPhone/AGENTS.md) |
|
|
66
|
+
| DatePicker | [../../src/components/DatePicker/AGENTS.md](../../src/components/DatePicker/AGENTS.md) |
|
|
67
|
+
| DateRangePicker | [../../src/components/DateRangePicker/AGENTS.md](../../src/components/DateRangePicker/AGENTS.md) |
|
|
68
|
+
| InputNumber | [../../src/components/InputNumber/AGENTS.md](../../src/components/InputNumber/AGENTS.md) |
|
|
69
|
+
| TimePicker | [../../src/components/TimePicker/AGENTS.md](../../src/components/TimePicker/AGENTS.md) |
|
|
70
|
+
| Text | [../../src/components/Text/AGENTS.md](../../src/components/Text/AGENTS.md) |
|
|
71
|
+
| DateTimePicker | [../../src/components/DateTimePicker/AGENTS.md](../../src/components/DateTimePicker/AGENTS.md) |
|
|
72
|
+
| Tooltip | [../../src/components/Tooltip/AGENTS.md](../../src/components/Tooltip/AGENTS.md) |
|
|
73
|
+
| Toggle | [../../src/components/Toggle/AGENTS.md](../../src/components/Toggle/AGENTS.md) |
|
|
74
|
+
| Notice | [../../src/components/Notice/AGENTS.md](../../src/components/Notice/AGENTS.md) |
|
|
75
|
+
| SortableList | [../../src/components/SortableList/AGENTS.md](../../src/components/SortableList/AGENTS.md) |
|
|
76
|
+
|
|
77
|
+
Not yet part of the documented public export surface: `Progress` (source exists but is incomplete — see Known Limitations).
|
|
78
|
+
|
|
79
|
+
## Code Block Rendering
|
|
80
|
+
|
|
81
|
+
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:
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
// Inline code — renders with pill/badge style
|
|
85
|
+
<code>someValue</code>
|
|
86
|
+
|
|
87
|
+
// Block code — renders with dark background, padding, and border-radius
|
|
88
|
+
<code><pre>{`your
|
|
89
|
+
multiline
|
|
90
|
+
code here`}</pre></code>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The two modes are driven by `_code.scss`:
|
|
94
|
+
- `<code>` alone → light gray background, inline display
|
|
95
|
+
- `<code>` containing `<pre>` → dark background (`--gray800`), block display, padded and rounded
|
|
96
|
+
|
|
97
|
+
Do not create `preStyle`, `codeBlockStyle`, or equivalent inline style objects for this purpose. The global styles handle it.
|
|
98
|
+
|
|
99
|
+
## Toast (react-toastify)
|
|
100
|
+
|
|
101
|
+
Craftsman re-exports `toast` and `ToastContainer` from `react-toastify`. No custom wrapper is needed, and there is no dedicated component folder for it.
|
|
102
|
+
|
|
103
|
+
Import:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import { toast, ToastContainer } from "react-toastify";
|
|
107
|
+
import "react-toastify/dist/ReactToastify.css";
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Usage:
|
|
111
|
+
|
|
112
|
+
- Render one `<ToastContainer>` near the root of your app.
|
|
113
|
+
- Call `toast(...)` anywhere in response to user actions.
|
|
114
|
+
- `ToastContainer` props: `position`, `autoClose`, `theme` (`"light" | "dark" | "colored"`), `closeOnClick`, `pauseOnHover`, `draggable`, `newestOnTop`.
|
|
115
|
+
|
|
116
|
+
Example:
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
// Root layout
|
|
120
|
+
<ToastContainer position="bottom-right" autoClose={3000} theme="light" />
|
|
121
|
+
|
|
122
|
+
// Anywhere in the app
|
|
123
|
+
toast("Saved successfully!");
|
|
124
|
+
toast.error("Something went wrong.");
|
|
125
|
+
toast.success("Profile updated.");
|
|
126
|
+
toast.warning("Unsaved changes.");
|
|
127
|
+
toast.info("New version available.");
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Code Generation Patterns to Prefer
|
|
131
|
+
|
|
132
|
+
1. Generate fully typed React usage examples.
|
|
133
|
+
2. Keep accessibility props in place (`aria-label`, `disabled`, semantic `type`).
|
|
134
|
+
3. Use `variant="primary"` for main actions and `variant="text"` for low-emphasis actions.
|
|
135
|
+
4. For loading states, pair `Loader` with accessible status text where needed.
|
|
136
|
+
5. **Functional component declaration order** — always organize the body in this sequence:
|
|
137
|
+
1. Hook calls (`useSomething`)
|
|
138
|
+
2. State (`useState`)
|
|
139
|
+
3. Derived state / variables (values computed from state or props)
|
|
140
|
+
4. Action handlers (`const handle*`, `const dispatch*`, `useEffect`)
|
|
141
|
+
|
|
142
|
+
## Known Limitations (Current Package State)
|
|
143
|
+
|
|
144
|
+
1. The README now covers installation, theming, and getting-started usage; treat this skill and the per-component `AGENTS.md` files as the source of truth for deeper agent usage.
|
|
145
|
+
2. Theme utilities exist in source but are not guaranteed public package exports.
|
|
146
|
+
3. `Progress` exists in source but is incomplete and intentionally omitted from documentation for now.
|
|
147
|
+
|
|
148
|
+
## Safe Fallback Strategy for Agents
|
|
149
|
+
|
|
150
|
+
If uncertain about available exports:
|
|
151
|
+
|
|
152
|
+
1. Use only `Button`, `Checkbox`, `DatePicker`, `Input`, `InputPassword`, `InputPhone`, `Loader`, `Modal`, `RadioButton`, `Select`, `Text`, and `Textarea` from their component entry points.
|
|
153
|
+
2. Do not invent package APIs.
|
|
154
|
+
3. Prefer native HTML elements for anything not explicitly exported.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craftsman-device-detection
|
|
3
|
+
description: 'Device/browser detection conventions for @stamcat/craftsman — use react-device-detect instead of hand-rolled user-agent sniffing or window.innerWidth checks. Use when writing responsive logic, conditional rendering by device/browser/OS, or reviewing code that branches on viewport size or navigator.userAgent.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Craftsman Device Detection
|
|
7
|
+
|
|
8
|
+
`react-device-detect` is already a dependency of `@stamcat/craftsman` (it powers the mobile wheel-picker fallback in `TimePicker`). **Prefer it over custom implementations** for any device, browser, or OS branching logic. Consuming apps should install it directly rather than writing their own detection.
|
|
9
|
+
|
|
10
|
+
## Hard Rule
|
|
11
|
+
|
|
12
|
+
Never write custom detection logic such as:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// DO NOT — fragile, reinvents an already-solved problem
|
|
16
|
+
const isMobile = /Mobi|Android/i.test(navigator.userAgent);
|
|
17
|
+
const isMobile = window.innerWidth < 768;
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Use the library instead:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { isMobile } from "react-device-detect";
|
|
24
|
+
|
|
25
|
+
if (isMobile) {
|
|
26
|
+
// mobile-specific behavior
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
CSS media queries are still the right tool for pure layout/visual breakpoints (see the [craftsman-style-utilities skill](../craftsman-style-utilities/SKILL.md) for `breakpoint()`). Reach for `react-device-detect` when the decision depends on the actual device/browser/OS rather than viewport width alone, or when the branch happens in JS/TS rather than CSS.
|
|
31
|
+
|
|
32
|
+
## Common Named Exports
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import {
|
|
36
|
+
isMobile,
|
|
37
|
+
isTablet,
|
|
38
|
+
isDesktop,
|
|
39
|
+
isBrowser,
|
|
40
|
+
isSmartTV,
|
|
41
|
+
isAndroid,
|
|
42
|
+
isIOS,
|
|
43
|
+
isMobileOnly,
|
|
44
|
+
browserName,
|
|
45
|
+
osName,
|
|
46
|
+
deviceType,
|
|
47
|
+
BrowserView,
|
|
48
|
+
MobileView,
|
|
49
|
+
TabletView,
|
|
50
|
+
} from "react-device-detect";
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- `isMobile` / `isTablet` / `isDesktop` / `isBrowser` — boolean flags for conditional logic.
|
|
54
|
+
- `isMobileOnly` — `true` for phones, `false` for tablets (use this, not `isMobile`, when tablets must be excluded).
|
|
55
|
+
- `browserName`, `osName`, `deviceType` — string values for logging/analytics, not for branching (prefer the boolean flags for that).
|
|
56
|
+
- `BrowserView` / `MobileView` / `TabletView` — declarative wrapper components that render `children` only on the matching device, as an alternative to `if (isMobile)` branches in JSX.
|
|
57
|
+
|
|
58
|
+
## Example: Conditional Rendering
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { MobileView, BrowserView } from "react-device-detect";
|
|
62
|
+
|
|
63
|
+
<MobileView>
|
|
64
|
+
<CompactNav />
|
|
65
|
+
</MobileView>
|
|
66
|
+
<BrowserView>
|
|
67
|
+
<FullNav />
|
|
68
|
+
</BrowserView>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Orientation
|
|
72
|
+
|
|
73
|
+
For portrait/landscape detection, use the `useMobileOrientation` hook rather than comparing `window.innerWidth`/`innerHeight`:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { useMobileOrientation } from "react-device-detect";
|
|
77
|
+
|
|
78
|
+
const { isPortrait, isLandscape } = useMobileOrientation();
|
|
79
|
+
```
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craftsman-style-utilities
|
|
3
|
+
description: 'Styling conventions for @stamcat/craftsman — color()/width()/breakpoint() TypeScript and Sass helpers, styling expectations, and theme authoring (theme.root, theme.components, theme.widths). Use when writing styles, CSS-in-JS, Sass, or ThemeProvider theme objects for @stamcat/craftsman.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Craftsman Style Utilities
|
|
7
|
+
|
|
8
|
+
Styling rules for AI agents generating code against `@stamcat/craftsman`. For package exports, hard rules, and component contracts, see the [craftsman-component-usage skill](../craftsman-component-usage/SKILL.md). For non-styling utility functions (`isEmpty`), see the [craftsman-utility-functions skill](../craftsman-utility-functions/SKILL.md).
|
|
9
|
+
|
|
10
|
+
## Sass Import Path (hard rule)
|
|
11
|
+
|
|
12
|
+
Always `@use` the published subpath — never deep-import the source file directly.
|
|
13
|
+
|
|
14
|
+
```scss
|
|
15
|
+
// Correct
|
|
16
|
+
@use "@stamcat/craftsman/styles/utilities/functions" as u;
|
|
17
|
+
@use "@stamcat/craftsman/styles/utilities/mixins" as m;
|
|
18
|
+
@use "@stamcat/craftsman/styles/utilities/placeholders";
|
|
19
|
+
|
|
20
|
+
// Wrong — not an exported subpath, will fail to resolve at build time
|
|
21
|
+
@use "@stamcat/craftsman/src/styles/utilities/functions" as u;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Style Utilities
|
|
25
|
+
|
|
26
|
+
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.
|
|
27
|
+
|
|
28
|
+
### color()
|
|
29
|
+
|
|
30
|
+
**TypeScript** — import from `@stamcat/craftsman/styles`:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { color, colors, hexToRgba } from "@stamcat/craftsman/styles";
|
|
34
|
+
|
|
35
|
+
// Returns var(--blue500)
|
|
36
|
+
color("blue500")
|
|
37
|
+
|
|
38
|
+
// Returns rgb(from var(--blue500) r g b / 0.5)
|
|
39
|
+
color("blue500", "rgba", 0.5)
|
|
40
|
+
|
|
41
|
+
// Fallback for environments that don't support CSS relative color syntax
|
|
42
|
+
hexToRgba(colors.blue500, 0.5)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Sass** — import `functions` as `u`:
|
|
46
|
+
|
|
47
|
+
```scss
|
|
48
|
+
@use "@stamcat/craftsman/styles/utilities/functions" as u;
|
|
49
|
+
|
|
50
|
+
.element {
|
|
51
|
+
color: #{u.color(blue500)};
|
|
52
|
+
background: #{u.color(black, rgba, 0.4)};
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- `color(name)` → `var(--name)` — always prefer this over hard-coded hex values so theme overrides apply.
|
|
57
|
+
- `color(name, rgba, alpha)` → `rgb(from var(--name) r g b / alpha)` — uses CSS relative color syntax; verify browser support for your target.
|
|
58
|
+
- Never hard-code hex color values. Always use `color()` or a CSS variable.
|
|
59
|
+
|
|
60
|
+
### width()
|
|
61
|
+
|
|
62
|
+
**TypeScript** — import from `@stamcat/craftsman/styles`:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { width } from "@stamcat/craftsman/styles";
|
|
66
|
+
|
|
67
|
+
width("gutter") // var(--w-gutter, 16px)
|
|
68
|
+
width("gutter", 0.5) // calc(var(--w-gutter, 16px) * 0.5)
|
|
69
|
+
width("column", 3) // calc((var(--w-column) * 3) + (var(--w-gutter) * 2))
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Sass** — import `functions` as `u`:
|
|
73
|
+
|
|
74
|
+
```scss
|
|
75
|
+
@use "@stamcat/craftsman/styles/utilities/functions" as u;
|
|
76
|
+
|
|
77
|
+
.card {
|
|
78
|
+
padding: #{u.width(gutter)};
|
|
79
|
+
gap: #{u.width(gutter, 0.5)};
|
|
80
|
+
max-width: #{u.width(column, 4)};
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Valid keys: `text` · `gutter` · `column` · `tablet` · `desktop` · `extDesktop` · `mobileMax` · `tabletMax` · `desktopMax`
|
|
85
|
+
|
|
86
|
+
- The `column` key automatically accounts for gutters between columns.
|
|
87
|
+
- Never use hard-coded `px` values for spacing or layout widths. Always use `width()`.
|
|
88
|
+
|
|
89
|
+
### breakpoint()
|
|
90
|
+
|
|
91
|
+
**TypeScript** — returns a full `@media` rule string for CSS-in-JS:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { breakpoint, media } from "@stamcat/craftsman/styles";
|
|
95
|
+
|
|
96
|
+
// Full rule — use inside styled-components, emotion, or style injection
|
|
97
|
+
breakpoint("desktop", "font-size: 18px;")
|
|
98
|
+
// => "@media (min-width: 1040px) { font-size: 18px; }"
|
|
99
|
+
|
|
100
|
+
// Raw query string only — use for conditional logic or matchMedia
|
|
101
|
+
media.tablet // => "(min-width: 660px)"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Sass** — `@include breakpoint(key)` wraps content in the correct `@media` query:
|
|
105
|
+
|
|
106
|
+
```scss
|
|
107
|
+
@use "@stamcat/craftsman/styles/utilities/functions" as u;
|
|
108
|
+
|
|
109
|
+
.sidebar {
|
|
110
|
+
display: none;
|
|
111
|
+
|
|
112
|
+
@include u.breakpoint(tablet) {
|
|
113
|
+
display: block;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Available breakpoint keys: `tablet` · `tabletMax` · `tabletOnly` · `desktop` · `desktopMax` · `desktopOnly` · `extDesktop` · `mobileMax` · `mobileOnly` · `mobileTablet`
|
|
119
|
+
|
|
120
|
+
- Breakpoint values come from `_config.scss` and stay in sync with any project overrides.
|
|
121
|
+
- Never hard-code `@media (min-width: 1040px)` or similar — always use `breakpoint()` so values stay consistent with config.
|
|
122
|
+
|
|
123
|
+
## Styling Expectations
|
|
124
|
+
|
|
125
|
+
- Components are built with SCSS modules and class-based variant hooks.
|
|
126
|
+
- If your app does not include this package's global CSS variable setup, visual output may differ.
|
|
127
|
+
- Agents should avoid hard-coding assumptions about token names beyond what the consumer app already defines.
|
|
128
|
+
|
|
129
|
+
## Custom Sizing / Scale Props
|
|
130
|
+
|
|
131
|
+
When a component accepts a numeric size/scale prop (for example `Button`'s `size` or `Toggle`'s `width`), the component sets **one** CSS custom property inline (only when the prop is provided), and the SCSS file does all the `calc()` math from there:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
// Component: only ever set the single scale variable
|
|
135
|
+
const sizeStyle = normalizedSize ? ({ "--btn-size": normalizedSize } as React.CSSProperties) : undefined;
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```scss
|
|
139
|
+
// SCSS: give the variable a default, then derive every scaled property from it
|
|
140
|
+
%button-styles {
|
|
141
|
+
--btn-size: 1;
|
|
142
|
+
padding: calc(#{u.width(gutter, 0.5)} * var(--btn-size)) calc(#{u.width(gutter, 0.75)} * var(--btn-size));
|
|
143
|
+
border-radius: calc(#{u.width(gutter, 0.75)} * var(--btn-size));
|
|
144
|
+
font-size: max(10px, calc(#{u.width(text)} * var(--btn-size)));
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Do not compute `calc()` strings per-property in JS/TS (for example building a `padding: "calc(... * 1.5)"` string in the component). Always push that math into SCSS and only pass the raw scale/size value through a single CSS variable.
|
|
149
|
+
|
|
150
|
+
## Theme Authoring
|
|
151
|
+
|
|
152
|
+
- `theme.root` supports JS style objects and raw CSS/Sass strings.
|
|
153
|
+
- `theme.components.*` supports JS style objects and raw CSS/Sass strings.
|
|
154
|
+
- `theme.widths` accepts a partial record of width/breakpoint keys to override the default `--w-*` CSS variables. Values are numbers in `px`.
|
|
155
|
+
- String component styles are applied to the mapped target selector (for example `button`, `input[type='checkbox']`).
|
|
156
|
+
- For multi-file Sass workflows with syntax highlighting and mixins, import compiled CSS text via `*.scss?inline`.
|
|
157
|
+
|
|
158
|
+
Valid `theme.widths` keys: `"text" | "gutter" | "column" | "tablet" | "desktop" | "extDesktop" | "mobileMax" | "tabletMax" | "desktopMax"`
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import greenRoot from "./green.root.scss?inline";
|
|
164
|
+
import greenButton from "./green.button.scss?inline";
|
|
165
|
+
|
|
166
|
+
export const theme = {
|
|
167
|
+
widths: {
|
|
168
|
+
gutter: 20,
|
|
169
|
+
tablet: 768,
|
|
170
|
+
},
|
|
171
|
+
root: greenRoot,
|
|
172
|
+
components: {
|
|
173
|
+
button: greenButton,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craftsman-utility-functions
|
|
3
|
+
description: 'Utility function conventions for @stamcat/craftsman — the isEmpty validation helper and TypeScript/Sass parity for style utility patterns. Use when writing empty/falsy checks or referencing color()/width()/breakpoint() usage patterns for @stamcat/craftsman.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Craftsman Utility Functions
|
|
7
|
+
|
|
8
|
+
Non-styling utility function rules for AI agents generating code against `@stamcat/craftsman`. For package exports, hard rules, and component contracts, see the [craftsman-component-usage skill](../craftsman-component-usage/SKILL.md). For styling and theme authoring, see the [craftsman-style-utilities skill](../craftsman-style-utilities/SKILL.md).
|
|
9
|
+
|
|
10
|
+
## `isEmpty`
|
|
11
|
+
|
|
12
|
+
The package exports an `isEmpty` utility. **Always use it instead of writing inline empty checks.**
|
|
13
|
+
|
|
14
|
+
Import:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { isEmpty } from "@stamcat/craftsman/utilities/validations";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
It returns `true` for:
|
|
21
|
+
|
|
22
|
+
- `undefined`
|
|
23
|
+
- `null`
|
|
24
|
+
- empty objects — `{}`
|
|
25
|
+
- strings that are empty or whitespace-only — `""`, `" "`
|
|
26
|
+
- arrays with no elements — `[]`
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// DO — use isEmpty
|
|
32
|
+
if (isEmpty(value)) { ... }
|
|
33
|
+
if (!isEmpty(items)) { ... }
|
|
34
|
+
|
|
35
|
+
// DO NOT — write these manually
|
|
36
|
+
if (value === undefined || value === null) { ... }
|
|
37
|
+
if (typeof value === "string" && value.trim().length === 0) { ... }
|
|
38
|
+
if (Object.keys(obj).length === 0) { ... }
|
|
39
|
+
if (arr.length === 0) { ... }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Style utilities parity (`color`, `width`, `breakpoint`)
|
|
43
|
+
|
|
44
|
+
These utility patterns exist in both TypeScript and Sass. See the [craftsman-style-utilities skill](../craftsman-style-utilities/SKILL.md) for full usage details.
|
|
45
|
+
|
|
46
|
+
TypeScript usage:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { color, width, breakpoint } from "@stamcat/craftsman/styles";
|
|
50
|
+
|
|
51
|
+
const accent = color("blue500");
|
|
52
|
+
const alphaAccent = color("blue500", "rgba", 0.32);
|
|
53
|
+
const twoColumns = width("column", 2);
|
|
54
|
+
const mobileRule = breakpoint("mobileMax", "h4{font-size:14px;}");
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Sass usage (framework source):
|
|
58
|
+
|
|
59
|
+
```scss
|
|
60
|
+
@use "./src/styles/utilities" as u;
|
|
61
|
+
|
|
62
|
+
.example {
|
|
63
|
+
color: #{u.color(blue500)};
|
|
64
|
+
background: #{u.color(blue500, rgba, 0.32)};
|
|
65
|
+
max-width: #{u.width(column, 2)};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@include u.breakpoint(mobileMax) {
|
|
69
|
+
.example { font-size: #{u.width(text)}; }
|
|
70
|
+
}
|
|
71
|
+
```
|