@idealyst/theme 1.0.83 → 1.0.84
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/package.json +2 -1
- package/src/components/CLAUDE.md +468 -0
- package/src/components/accordion.ts +34 -0
- package/src/components/activity-indicator.ts +28 -0
- package/src/components/alert.ts +32 -0
- package/src/components/avatar.ts +27 -0
- package/src/components/badge.ts +28 -0
- package/src/components/breadcrumb.ts +36 -0
- package/src/components/button.ts +32 -0
- package/src/components/card.ts +31 -0
- package/src/components/checkbox.ts +37 -0
- package/src/components/chip.ts +34 -0
- package/src/components/dialog.ts +31 -0
- package/src/components/divider.ts +36 -0
- package/src/components/icon.ts +26 -0
- package/src/components/image.ts +22 -0
- package/src/components/index.ts +37 -0
- package/src/components/input.ts +35 -0
- package/src/components/list.ts +40 -0
- package/src/components/menu-item.ts +29 -0
- package/src/components/menu.ts +32 -0
- package/src/components/popover.ts +25 -0
- package/src/components/pressable.ts +20 -0
- package/src/components/progress.ts +35 -0
- package/src/components/radio-button.ts +38 -0
- package/src/components/screen.ts +25 -0
- package/src/components/select.ts +62 -0
- package/src/components/skeleton.ts +26 -0
- package/src/components/slider.ts +62 -0
- package/src/components/svg-image.ts +24 -0
- package/src/components/switch.ts +54 -0
- package/src/components/tab-bar.ts +54 -0
- package/src/components/table.ts +57 -0
- package/src/components/text.ts +29 -0
- package/src/components/textarea.ts +53 -0
- package/src/components/tooltip.ts +29 -0
- package/src/components/video.ts +18 -0
- package/src/components/view.ts +31 -0
- package/src/darkTheme.ts +890 -0
- package/src/index.ts +7 -166
- package/src/lightTheme.ts +873 -0
- package/src/styles.ts +14 -0
- package/src/theme/color.ts +15 -0
- package/src/theme/index.ts +16 -0
- package/src/theme/intent.ts +8 -0
- package/src/theme/shadow.ts +18 -0
- package/src/theme/size.ts +182 -0
- package/src/theme/surface.ts +3 -0
- package/src/unistyles.ts +6 -14
- package/src/variants/color.ts +9 -0
- package/src/variants/index.ts +2 -0
- package/src/variants/intent.ts +16 -0
- package/src/variants/size.ts +0 -0
- package/CLAUDE.md +0 -447
- package/LLM-ACCESS-GUIDE.md +0 -208
- package/README.md +0 -633
- package/src/README.md +0 -138
- package/src/breakpoints.ts +0 -8
- package/src/colorResolver.ts +0 -218
- package/src/colors.md +0 -353
- package/src/colors.ts +0 -315
- package/src/common.ts +0 -92
- package/src/defaultThemes.md +0 -407
- package/src/defaultThemes.ts +0 -238
- package/src/themeBuilder.md +0 -400
- package/src/themeBuilder.ts +0 -602
- package/src/variantHelpers.ts +0 -584
- package/src/variants.ts +0 -56
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/theme",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.84",
|
|
4
4
|
"description": "Theming system for Idealyst Framework",
|
|
5
5
|
"readme": "README.md",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/react": "^19.1.0",
|
|
45
|
+
"react-native-unistyles": ">=3.0.0",
|
|
45
46
|
"typescript": "^5.0.0"
|
|
46
47
|
},
|
|
47
48
|
"files": [
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
# Component Styles Guide
|
|
2
|
+
|
|
3
|
+
This document outlines the best practices for implementing component styles in the Idealyst framework, based on the reference implementation in `@idealyst/theme/src/components/chip.ts`.
|
|
4
|
+
|
|
5
|
+
## Style System Architecture
|
|
6
|
+
|
|
7
|
+
### 1. Dynamic Styles with Variant Types
|
|
8
|
+
|
|
9
|
+
Use functions to programmatically define styles when variant properties have many possible values (like `intent`, `color`, `type`). This approach is more efficient and maintainable than defining each variant manually.
|
|
10
|
+
|
|
11
|
+
**Example:**
|
|
12
|
+
```typescript
|
|
13
|
+
function createContainerVariants(theme: Theme, intent: Intent, selected: boolean) {
|
|
14
|
+
const intentValue = theme.intents[intent];
|
|
15
|
+
const primaryColor = selected ? intentValue.contrast : intentValue.primary;
|
|
16
|
+
const secondaryColor = selected ? intentValue.primary : 'transparent';
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
filled: {
|
|
20
|
+
backgroundColor: primaryColor,
|
|
21
|
+
borderColor: secondaryColor,
|
|
22
|
+
},
|
|
23
|
+
outlined: {
|
|
24
|
+
backgroundColor: secondaryColor,
|
|
25
|
+
borderColor: primaryColor,
|
|
26
|
+
},
|
|
27
|
+
soft: {
|
|
28
|
+
backgroundColor: !selected ? intentValue.light : intentValue.primary,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Why this works:**
|
|
35
|
+
- Variables make the relationship between variants clear
|
|
36
|
+
- Easy to modify color logic in one place
|
|
37
|
+
- Reduces code duplication
|
|
38
|
+
- Scales well when adding new intents or colors
|
|
39
|
+
|
|
40
|
+
### 2. Avoid Explicit Return Types on Style Functions
|
|
41
|
+
|
|
42
|
+
Do **not** specify explicit return types for individual style creation functions. As long as the style conforms to `ExpandedComponentStyles`, it's better to avoid the ambiguous type since styles can be either objects or functions.
|
|
43
|
+
|
|
44
|
+
**Good:**
|
|
45
|
+
```typescript
|
|
46
|
+
const createContainerStyles = (theme: Theme, expanded: Partial<ExpandedChipStyles>) => {
|
|
47
|
+
return ({ intent, selected }: ChipVariants) => {
|
|
48
|
+
return deepMerge({
|
|
49
|
+
display: 'flex',
|
|
50
|
+
variants: {
|
|
51
|
+
size: createContainerSizeVariants(theme),
|
|
52
|
+
type: createContainerVariants(theme, intent, selected),
|
|
53
|
+
},
|
|
54
|
+
}, expanded);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Avoid:**
|
|
60
|
+
```typescript
|
|
61
|
+
const createContainerStyles = (theme: Theme, expanded: Partial<ExpandedChipStyles>): ExpandedChipStyles => {
|
|
62
|
+
// TypeScript might complain about object vs function return types
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 3. Proper Type Definitions
|
|
67
|
+
|
|
68
|
+
Each component stylesheet should define:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// 1. Variant value types
|
|
72
|
+
type ChipSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
73
|
+
type ChipType = 'filled' | 'outlined' | 'soft';
|
|
74
|
+
type ChipIntent = Intent;
|
|
75
|
+
|
|
76
|
+
// 2. Complete variant definition
|
|
77
|
+
type ChipVariants = {
|
|
78
|
+
size: ChipSize;
|
|
79
|
+
type: ChipType;
|
|
80
|
+
intent: ChipIntent;
|
|
81
|
+
selected: boolean;
|
|
82
|
+
disabled: boolean;
|
|
83
|
+
selectable: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 3. Expanded styles type for a single element
|
|
87
|
+
export type ExpandedChipStyles = StylesheetStyles<keyof ChipVariants>;
|
|
88
|
+
|
|
89
|
+
// 4. Complete stylesheet structure
|
|
90
|
+
export type ChipStylesheet = {
|
|
91
|
+
container: ExpandedChipStyles;
|
|
92
|
+
label: ExpandedChipStyles;
|
|
93
|
+
icon: ExpandedChipStyles;
|
|
94
|
+
deleteButton: ExpandedChipStyles;
|
|
95
|
+
deleteIcon: ExpandedChipStyles;
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 4. Size Values Must Be in Theme
|
|
100
|
+
|
|
101
|
+
**All size-related values** should be defined in `@idealyst/theme/src/theme/size.ts` with proper typings.
|
|
102
|
+
|
|
103
|
+
#### When to Add to Size Theme:
|
|
104
|
+
|
|
105
|
+
Add a new component size type when your component uses size variants:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// In theme/src/theme/size.ts
|
|
109
|
+
export type AllComponentSizes = {
|
|
110
|
+
button: Record<Size, ButtonSizeValue>;
|
|
111
|
+
chip: Record<Size, ChipSizeValue>;
|
|
112
|
+
badge: Record<Size, BadgeSizeValue>;
|
|
113
|
+
// Add your component here
|
|
114
|
+
yourComponent: Record<Size, YourComponentSizeValue>;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export type YourComponentSizeValue = {
|
|
118
|
+
paddingVertical: SizeValue;
|
|
119
|
+
paddingHorizontal: SizeValue;
|
|
120
|
+
fontSize: SizeValue;
|
|
121
|
+
// etc.
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### Using Size Values:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
function createSizeVariants(theme: Theme) {
|
|
129
|
+
return buildSizeVariants(theme, 'chip', (size) => ({
|
|
130
|
+
paddingHorizontal: size.paddingHorizontal,
|
|
131
|
+
paddingVertical: size.paddingVertical,
|
|
132
|
+
minHeight: size.minHeight,
|
|
133
|
+
borderRadius: size.borderRadius,
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 5. Theme vs Local Styles
|
|
139
|
+
|
|
140
|
+
**Theme provides:**
|
|
141
|
+
|
|
142
|
+
#### Colors
|
|
143
|
+
- **Intents**: `theme.intents[intent].primary`, `.contrast`, `.light`, `.dark`
|
|
144
|
+
- Available intents: `primary`, `success`, `error`, `warning`, `neutral`, `info`
|
|
145
|
+
- **Surface**: `theme.colors.surface.primary`, `.secondary`, `.tertiary`, `.inverse`, etc.
|
|
146
|
+
- **Text**: `theme.colors.text.primary`, `.secondary`, `.tertiary`, `.inverse`, etc.
|
|
147
|
+
- **Border**: `theme.colors.border.primary`, `.secondary`, `.tertiary`, `.disabled`
|
|
148
|
+
- **Palette**: `theme.colors.pallet[color][shade]` (e.g., `theme.colors.pallet.blue[500]`)
|
|
149
|
+
|
|
150
|
+
#### Sizes
|
|
151
|
+
- Component-specific size values via `buildSizeVariants(theme, 'componentName', callback)`
|
|
152
|
+
- Access specific size values: `theme.sizes.button[size].paddingVertical`, etc.
|
|
153
|
+
|
|
154
|
+
**Define locally in component styles:**
|
|
155
|
+
- Layout properties (flexDirection, alignItems, justifyContent)
|
|
156
|
+
- Display properties
|
|
157
|
+
- Positioning
|
|
158
|
+
- Component-specific constants that don't vary with theme (gap, borderRadius, margins)
|
|
159
|
+
- Typography that's not size-dependent (fontWeight, fontFamily)
|
|
160
|
+
- Transitions and animations
|
|
161
|
+
|
|
162
|
+
**IMPORTANT - Invalid Theme Properties:**
|
|
163
|
+
These properties do NOT exist in the theme and should be defined locally:
|
|
164
|
+
- ❌ `theme.spacing` - Use local constants (e.g., `gap: 8`)
|
|
165
|
+
- ❌ `theme.borderRadius` - Use local constants (e.g., `borderRadius: 4`)
|
|
166
|
+
- ❌ `theme.typography` - Use local constants for fontSize (e.g., `fontSize: 14`)
|
|
167
|
+
- ❌ `theme.colors?.text?.disabled` - Use opacity instead (e.g., `disabled: { opacity: 0.5 }`)
|
|
168
|
+
|
|
169
|
+
**Example of appropriate local definitions:**
|
|
170
|
+
```typescript
|
|
171
|
+
const createContainerStyles = (theme: Theme, expanded: Partial<ExpandedChipStyles>) => {
|
|
172
|
+
return ({ intent, selected }: ChipVariants) => {
|
|
173
|
+
return deepMerge({
|
|
174
|
+
display: 'flex', // Local
|
|
175
|
+
flexDirection: 'row', // Local
|
|
176
|
+
alignItems: 'center', // Local
|
|
177
|
+
justifyContent: 'center', // Local
|
|
178
|
+
gap: 4, // Local constant
|
|
179
|
+
variants: {
|
|
180
|
+
size: createSizeVariants(theme), // From theme
|
|
181
|
+
type: createTypeVariants(theme, intent), // Uses theme colors
|
|
182
|
+
},
|
|
183
|
+
}, expanded);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 6. Web-Specific Interactions (Hover & Active States)
|
|
189
|
+
|
|
190
|
+
For interactive components (buttons, checkboxes, links, etc.), include hover and active states in `_web` properties for better user experience on web platforms.
|
|
191
|
+
|
|
192
|
+
**Important Considerations:**
|
|
193
|
+
|
|
194
|
+
1. **Only add hover/active states to interactive components** - Components that users can click, press, or interact with
|
|
195
|
+
2. **Respect disabled state** - Don't apply hover/active effects when the component is disabled
|
|
196
|
+
3. **Use appropriate feedback** - Typically opacity changes, color shifts, or transforms
|
|
197
|
+
4. **Include both _hover and _active** - `_hover` for mouse over, `_active` for press/click down
|
|
198
|
+
|
|
199
|
+
**Pattern with Disabled State:**
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const createButtonStyles = (theme: Theme, expanded: Partial<ExpandedButtonStyles>) => {
|
|
203
|
+
return ({ intent }: ButtonVariants) => {
|
|
204
|
+
return deepMerge({
|
|
205
|
+
// ... base styles
|
|
206
|
+
variants: {
|
|
207
|
+
disabled: {
|
|
208
|
+
true: {
|
|
209
|
+
opacity: 0.6,
|
|
210
|
+
_web: {
|
|
211
|
+
cursor: 'not-allowed', // Show disabled cursor
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
false: {
|
|
215
|
+
opacity: 1,
|
|
216
|
+
_web: {
|
|
217
|
+
cursor: 'pointer',
|
|
218
|
+
_hover: { // ✓ Only when NOT disabled
|
|
219
|
+
opacity: 0.90,
|
|
220
|
+
},
|
|
221
|
+
_active: {
|
|
222
|
+
opacity: 0.75,
|
|
223
|
+
},
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
}, expanded);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**IMPORTANT - Unistyles Pseudo-Class Syntax:**
|
|
234
|
+
- Use `_hover`, `_active`, `_focus`, NOT `:hover`, `:active`, `:focus`
|
|
235
|
+
- Unistyles uses underscore prefix for pseudo-classes in `_web` properties
|
|
236
|
+
|
|
237
|
+
**Pattern for Always-Interactive Components:**
|
|
238
|
+
|
|
239
|
+
For components that don't have a disabled state:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
const createContainerStyles = (theme: Theme, expanded: Partial<ExpandedStyles>) => {
|
|
243
|
+
return deepMerge({
|
|
244
|
+
// ... base styles
|
|
245
|
+
_web: {
|
|
246
|
+
cursor: 'pointer',
|
|
247
|
+
transition: 'all 0.2s ease',
|
|
248
|
+
_hover: {
|
|
249
|
+
opacity: 0.8,
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
}, expanded);
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Interactive Components that Should Include _hover and _active:**
|
|
257
|
+
- Buttons (when not disabled)
|
|
258
|
+
- Checkboxes (when not disabled)
|
|
259
|
+
- Radio buttons (when not disabled)
|
|
260
|
+
- Links
|
|
261
|
+
- Pressable containers
|
|
262
|
+
- Chips (when selectable/deletable)
|
|
263
|
+
- Menu items
|
|
264
|
+
- List items (when clickable)
|
|
265
|
+
- Tab items
|
|
266
|
+
- Toggle switches (when not disabled)
|
|
267
|
+
|
|
268
|
+
**Non-Interactive Components (No Hover):**
|
|
269
|
+
- Text labels
|
|
270
|
+
- Icons (unless clickable)
|
|
271
|
+
- Dividers
|
|
272
|
+
- Progress indicators
|
|
273
|
+
- Badges (display-only)
|
|
274
|
+
- Static containers
|
|
275
|
+
|
|
276
|
+
### 7. Main Export Pattern
|
|
277
|
+
|
|
278
|
+
Always export a `create[Component]Stylesheet` function that:
|
|
279
|
+
- Takes `theme` as first parameter
|
|
280
|
+
- Takes optional `expanded` parameter for style overrides
|
|
281
|
+
- Returns the complete stylesheet object
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
export const createChipStylesheet = (
|
|
285
|
+
theme: Theme,
|
|
286
|
+
expanded?: Partial<ChipStylesheet>
|
|
287
|
+
): ChipStylesheet => {
|
|
288
|
+
return {
|
|
289
|
+
container: createContainerStyles(theme, expanded?.container || {}),
|
|
290
|
+
label: createLabelStyles(theme, expanded?.label || {}),
|
|
291
|
+
icon: createIconStyles(theme, expanded?.icon || {}),
|
|
292
|
+
deleteButton: createDeleteButtonStyles(theme, expanded?.deleteButton || {}),
|
|
293
|
+
deleteIcon: createDeleteIconStyles(theme, expanded?.deleteIcon || {}),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### 8. Use deepMerge for Extensibility
|
|
299
|
+
|
|
300
|
+
Always use `deepMerge` to combine base styles with expanded/custom styles:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import { deepMerge } from "../util/deepMerge";
|
|
304
|
+
|
|
305
|
+
const createStyles = (theme: Theme, expanded: Partial<ExpandedStyles>) => {
|
|
306
|
+
return deepMerge({
|
|
307
|
+
// base styles
|
|
308
|
+
}, expanded);
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
This allows consumers to override or extend any part of the stylesheet.
|
|
313
|
+
|
|
314
|
+
### 9. Using Dynamic Styles in Components
|
|
315
|
+
|
|
316
|
+
**CRITICAL:** When style functions return functions (dynamic styles), they **must be called** with their required variant properties when used in components.
|
|
317
|
+
|
|
318
|
+
#### Pattern for Dynamic Styles
|
|
319
|
+
|
|
320
|
+
If a style function depends on variant properties and returns a function:
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
// In component theme file (button.ts)
|
|
324
|
+
const createButtonStyles = (theme: Theme, expanded: Partial<ExpandedButtonStyles>) => {
|
|
325
|
+
return ({ intent }: ButtonVariants) => { // ← Returns a function that needs intent
|
|
326
|
+
return deepMerge({
|
|
327
|
+
variants: {
|
|
328
|
+
type: createButtonTypeVariants(theme, intent), // Uses intent dynamically
|
|
329
|
+
},
|
|
330
|
+
}, expanded);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Then in the component file, you **must call it** with the required properties:
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
// In Button.web.tsx (CORRECT)
|
|
339
|
+
const buttonStyleArray = [
|
|
340
|
+
buttonStyles.button({ intent }), // ✓ Calling with intent
|
|
341
|
+
buttonStyles.text({ intent }), // ✓ Calling with intent
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
// WRONG - will not work:
|
|
345
|
+
const buttonStyleArray = [
|
|
346
|
+
buttonStyles.button, // ✗ Missing function call
|
|
347
|
+
];
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
#### Pattern for Static Styles
|
|
351
|
+
|
|
352
|
+
If a style function returns a static object (no dynamic dependencies):
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
// In component theme file
|
|
356
|
+
const createButtonIconContainerStyles = (theme: Theme, expanded: Partial<ExpandedButtonStyles>) => {
|
|
357
|
+
return deepMerge({ // ← Returns object directly, not a function
|
|
358
|
+
display: 'flex',
|
|
359
|
+
gap: 4,
|
|
360
|
+
}, expanded);
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Then use it directly without calling:
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
// In Button.web.tsx (CORRECT)
|
|
368
|
+
const iconContainerProps = getWebProps([buttonStyles.iconContainer]); // ✓ No call needed
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
#### Rule of Thumb
|
|
372
|
+
|
|
373
|
+
- **Dynamic style (returns function)**: Call with properties → `buttonStyles.button({ intent })`
|
|
374
|
+
- **Static style (returns object)**: Use directly → `buttonStyles.iconContainer`
|
|
375
|
+
|
|
376
|
+
Look at which properties the inner function destructures from variants - those are the properties you must pass when calling the style.
|
|
377
|
+
|
|
378
|
+
## Complete Example Structure
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
import { CompoundVariants, StylesheetStyles } from "../styles";
|
|
382
|
+
import { Theme } from "../theme";
|
|
383
|
+
import { Intent } from "../theme/intent";
|
|
384
|
+
import { deepMerge } from "../util/deepMerge";
|
|
385
|
+
import { buildSizeVariants } from "../variants/size";
|
|
386
|
+
|
|
387
|
+
// 1. Type definitions
|
|
388
|
+
type ComponentSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
389
|
+
type ComponentVariants = {
|
|
390
|
+
size: ComponentSize;
|
|
391
|
+
intent: Intent;
|
|
392
|
+
disabled: boolean;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export type ExpandedComponentStyles = StylesheetStyles<keyof ComponentVariants>;
|
|
396
|
+
|
|
397
|
+
export type ComponentStylesheet = {
|
|
398
|
+
container: ExpandedComponentStyles;
|
|
399
|
+
label: ExpandedComponentStyles;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// 2. Variant generators (for complex/dynamic styles)
|
|
403
|
+
function createVariants(theme: Theme, intent: Intent) {
|
|
404
|
+
const intentValue = theme.intents[intent];
|
|
405
|
+
return {
|
|
406
|
+
filled: { backgroundColor: intentValue.primary },
|
|
407
|
+
outlined: { borderColor: intentValue.primary },
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// 3. Individual style creators
|
|
412
|
+
const createContainerStyles = (theme: Theme, expanded: Partial<ExpandedComponentStyles>) => {
|
|
413
|
+
return ({ intent }: ComponentVariants) => {
|
|
414
|
+
return deepMerge({
|
|
415
|
+
display: 'flex',
|
|
416
|
+
variants: {
|
|
417
|
+
size: buildSizeVariants(theme, 'component', (size) => ({
|
|
418
|
+
padding: size.padding,
|
|
419
|
+
})),
|
|
420
|
+
disabled: {
|
|
421
|
+
true: { opacity: 0.5 },
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
}, expanded);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// 4. Main export
|
|
429
|
+
export const createComponentStylesheet = (
|
|
430
|
+
theme: Theme,
|
|
431
|
+
expanded?: Partial<ComponentStylesheet>
|
|
432
|
+
): ComponentStylesheet => {
|
|
433
|
+
return {
|
|
434
|
+
container: createContainerStyles(theme, expanded?.container || {}),
|
|
435
|
+
label: createLabelStyles(theme, expanded?.label || {}),
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Checklist for New Component Styles
|
|
441
|
+
|
|
442
|
+
### Theme Package (`@idealyst/theme/src/components/`)
|
|
443
|
+
- [ ] Define variant types (Size, Type, etc.)
|
|
444
|
+
- [ ] Define complete `ComponentVariants` type
|
|
445
|
+
- [ ] Export `ExpandedComponentStyles` type
|
|
446
|
+
- [ ] Export `ComponentStylesheet` type
|
|
447
|
+
- [ ] Add size values to `theme/src/theme/size.ts` if needed
|
|
448
|
+
- [ ] Create variant generator functions for complex/dynamic styles
|
|
449
|
+
- [ ] Create individual style functions (no explicit return type)
|
|
450
|
+
- [ ] Use `deepMerge` for all style combinations
|
|
451
|
+
- [ ] Use `buildSizeVariants` for size-related properties
|
|
452
|
+
- [ ] Export `create[Component]Stylesheet` function
|
|
453
|
+
- [ ] Use proper theme colors: `theme.colors.border`, `theme.colors.text`, `theme.colors.surface`, `theme.intents`
|
|
454
|
+
- [ ] **Avoid invalid theme properties**: No `theme.spacing`, `theme.borderRadius`, `theme.typography`, or optional chaining on colors
|
|
455
|
+
- [ ] Keep layout/display properties local, colors/sizes from theme
|
|
456
|
+
- [ ] **Add hover and active states for interactive components** (see section 6)
|
|
457
|
+
- Include `_hover` and `_active` (NOT `:hover`/`:active`) in `_web` properties for disabled `false` variant only
|
|
458
|
+
- Set `cursor: 'pointer'` for interactive elements, `cursor: 'not-allowed'` for disabled
|
|
459
|
+
- Use Unistyles syntax: `_hover`, `_active`, `_focus` (underscore prefix)
|
|
460
|
+
- Typical pattern: `_hover: { opacity: 0.8 }`, `_active: { opacity: 0.6 }`
|
|
461
|
+
|
|
462
|
+
### Component Package (`@idealyst/components/src/Component/`)
|
|
463
|
+
- [ ] **Update Component.web.tsx**: Call dynamic styles with required properties (see section 9)
|
|
464
|
+
- Example: `buttonStyles.button({ intent })` not `buttonStyles.button`
|
|
465
|
+
- [ ] **Update Component.native.tsx**: Call dynamic styles with required properties (see section 9)
|
|
466
|
+
- Example: `buttonStyles.text({ intent })` not `buttonStyles.text`
|
|
467
|
+
- [ ] Verify both web and native components use the same variant properties
|
|
468
|
+
- [ ] Test that all size variants work (xs, sm, md, lg, xl)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { StylesheetStyles } from "../styles";
|
|
2
|
+
import { Size } from "../theme/size";
|
|
3
|
+
|
|
4
|
+
type AccordionSize = Size;
|
|
5
|
+
type AccordionType = 'default' | 'separated' | 'bordered';
|
|
6
|
+
|
|
7
|
+
type AccordionVariants = {
|
|
8
|
+
size: AccordionSize;
|
|
9
|
+
type: AccordionType;
|
|
10
|
+
expanded: boolean;
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
isLast: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type ExpandedAccordionStyles = StylesheetStyles<keyof AccordionVariants>;
|
|
16
|
+
|
|
17
|
+
export type AccordionStylesheet = {
|
|
18
|
+
container: ExpandedAccordionStyles;
|
|
19
|
+
item: ExpandedAccordionStyles;
|
|
20
|
+
header: ExpandedAccordionStyles;
|
|
21
|
+
title: ExpandedAccordionStyles;
|
|
22
|
+
icon: ExpandedAccordionStyles;
|
|
23
|
+
content: ExpandedAccordionStyles;
|
|
24
|
+
contentInner: ExpandedAccordionStyles;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* NOTE: The accordion stylesheet implementation has been moved to
|
|
29
|
+
* @idealyst/components/src/Accordion/Accordion.styles.tsx
|
|
30
|
+
*
|
|
31
|
+
* This was necessary because Unistyles' Babel transform on native cannot resolve
|
|
32
|
+
* function calls to extract variant structures at compile time. The styles must be
|
|
33
|
+
* inlined directly in StyleSheet.create() for variants to work on native.
|
|
34
|
+
*/
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { StylesheetStyles } from "../styles";
|
|
2
|
+
import { Intent } from "../theme/intent";
|
|
3
|
+
import { Size } from "../theme/size";
|
|
4
|
+
|
|
5
|
+
type ActivityIndicatorSize = Size;
|
|
6
|
+
type ActivityIndicatorIntent = Intent;
|
|
7
|
+
|
|
8
|
+
type ActivityIndicatorVariants = {
|
|
9
|
+
size: ActivityIndicatorSize;
|
|
10
|
+
intent: ActivityIndicatorIntent;
|
|
11
|
+
animating: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type ExpandedActivityIndicatorStyles = StylesheetStyles<keyof ActivityIndicatorVariants>;
|
|
15
|
+
|
|
16
|
+
export type ActivityIndicatorStylesheet = {
|
|
17
|
+
container: ExpandedActivityIndicatorStyles;
|
|
18
|
+
spinner: ExpandedActivityIndicatorStyles;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* NOTE: The activity-indicator stylesheet implementation has been moved to
|
|
23
|
+
* @idealyst/components/src/ActivityIndicator/ActivityIndicator.styles.tsx
|
|
24
|
+
*
|
|
25
|
+
* This was necessary because Unistyles' Babel transform on native cannot resolve
|
|
26
|
+
* function calls to extract variant structures at compile time. The styles must be
|
|
27
|
+
* inlined directly in StyleSheet.create() for variants to work on native.
|
|
28
|
+
*/
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { StylesheetStyles } from "../styles";
|
|
2
|
+
import { Intent } from "../theme/intent";
|
|
3
|
+
|
|
4
|
+
type AlertType = 'filled' | 'outlined' | 'soft';
|
|
5
|
+
type AlertIntent = Intent | 'info';
|
|
6
|
+
|
|
7
|
+
type AlertVariants = {
|
|
8
|
+
type: AlertType;
|
|
9
|
+
intent: AlertIntent;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ExpandedAlertStyles = StylesheetStyles<keyof AlertVariants>;
|
|
13
|
+
|
|
14
|
+
export type AlertStylesheet = {
|
|
15
|
+
container: ExpandedAlertStyles;
|
|
16
|
+
iconContainer: ExpandedAlertStyles;
|
|
17
|
+
content: ExpandedAlertStyles;
|
|
18
|
+
title: ExpandedAlertStyles;
|
|
19
|
+
message: ExpandedAlertStyles;
|
|
20
|
+
actions: ExpandedAlertStyles;
|
|
21
|
+
closeButton: ExpandedAlertStyles;
|
|
22
|
+
closeIcon: ExpandedAlertStyles;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* NOTE: The alert stylesheet implementation has been moved to
|
|
27
|
+
* @idealyst/components/src/Alert/Alert.styles.tsx
|
|
28
|
+
*
|
|
29
|
+
* This was necessary because Unistyles' Babel transform on native cannot resolve
|
|
30
|
+
* function calls to extract variant structures at compile time. The styles must be
|
|
31
|
+
* inlined directly in StyleSheet.create() for variants to work on native.
|
|
32
|
+
*/
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { StylesheetStyles } from "../styles";
|
|
2
|
+
import { Size } from "../theme/size";
|
|
3
|
+
|
|
4
|
+
type AvatarSize = Size;
|
|
5
|
+
type AvatarShape = 'circle' | 'square';
|
|
6
|
+
|
|
7
|
+
type AvatarVariants = {
|
|
8
|
+
size: AvatarSize;
|
|
9
|
+
shape: AvatarShape;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ExpandedAvatarStyles = StylesheetStyles<keyof AvatarVariants>;
|
|
13
|
+
|
|
14
|
+
export type AvatarStylesheet = {
|
|
15
|
+
avatar: ExpandedAvatarStyles;
|
|
16
|
+
image: ExpandedAvatarStyles;
|
|
17
|
+
fallback: ExpandedAvatarStyles;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* NOTE: The avatar stylesheet implementation has been moved to
|
|
22
|
+
* @idealyst/components/src/Avatar/Avatar.styles.tsx
|
|
23
|
+
*
|
|
24
|
+
* This was necessary because Unistyles' Babel transform on native cannot resolve
|
|
25
|
+
* function calls to extract variant structures at compile time. The styles must be
|
|
26
|
+
* inlined directly in StyleSheet.create() for variants to work on native.
|
|
27
|
+
*/
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { StylesheetStyles } from "../styles";
|
|
2
|
+
import { Color, Size } from "../theme";
|
|
3
|
+
|
|
4
|
+
type BadgeType = 'filled' | 'outlined' | 'dot';
|
|
5
|
+
|
|
6
|
+
type BadgeVariants = {
|
|
7
|
+
size: Size;
|
|
8
|
+
type: BadgeType;
|
|
9
|
+
color: Color;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ExpandedBadgeStyles = StylesheetStyles<keyof BadgeVariants>;
|
|
13
|
+
|
|
14
|
+
export type BadgeStylesheet = {
|
|
15
|
+
badge: ExpandedBadgeStyles;
|
|
16
|
+
content: ExpandedBadgeStyles;
|
|
17
|
+
icon: ExpandedBadgeStyles;
|
|
18
|
+
text: ExpandedBadgeStyles;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* NOTE: The badge stylesheet implementation has been moved to
|
|
23
|
+
* @idealyst/components/src/Badge/Badge.styles.tsx
|
|
24
|
+
*
|
|
25
|
+
* This was necessary because Unistyles' Babel transform on native cannot resolve
|
|
26
|
+
* function calls to extract variant structures at compile time. The styles must be
|
|
27
|
+
* inlined directly in StyleSheet.create() for variants to work on native.
|
|
28
|
+
*/
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { StylesheetStyles } from "../styles";
|
|
2
|
+
import { Size } from "../theme/size";
|
|
3
|
+
|
|
4
|
+
type BreadcrumbSize = Size;
|
|
5
|
+
type BreadcrumbIntent = 'primary' | 'neutral';
|
|
6
|
+
|
|
7
|
+
type BreadcrumbVariants = {
|
|
8
|
+
size: BreadcrumbSize;
|
|
9
|
+
intent: BreadcrumbIntent;
|
|
10
|
+
disabled: boolean;
|
|
11
|
+
isLast: boolean;
|
|
12
|
+
clickable: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type ExpandedBreadcrumbStyles = StylesheetStyles<keyof BreadcrumbVariants>;
|
|
16
|
+
|
|
17
|
+
export type BreadcrumbStylesheet = {
|
|
18
|
+
container: ExpandedBreadcrumbStyles;
|
|
19
|
+
item: ExpandedBreadcrumbStyles;
|
|
20
|
+
itemText: ExpandedBreadcrumbStyles;
|
|
21
|
+
icon: ExpandedBreadcrumbStyles;
|
|
22
|
+
separator: ExpandedBreadcrumbStyles;
|
|
23
|
+
ellipsis: ExpandedBreadcrumbStyles;
|
|
24
|
+
ellipsisIcon: ExpandedBreadcrumbStyles;
|
|
25
|
+
menuButton: ExpandedBreadcrumbStyles;
|
|
26
|
+
menuButtonIcon: ExpandedBreadcrumbStyles;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* NOTE: The breadcrumb stylesheet implementation has been moved to
|
|
31
|
+
* @idealyst/components/src/Breadcrumb/Breadcrumb.styles.tsx
|
|
32
|
+
*
|
|
33
|
+
* This was necessary because Unistyles' Babel transform on native cannot resolve
|
|
34
|
+
* function calls to extract variant structures at compile time. The styles must be
|
|
35
|
+
* inlined directly in StyleSheet.create() for variants to work on native.
|
|
36
|
+
*/
|