stampd 0.1.0
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 +611 -0
- package/dist/analyzer/analyzeStyle.d.ts +13 -0
- package/dist/analyzer/analyzeStyle.d.ts.map +1 -0
- package/dist/analyzer/analyzeStyle.js +43 -0
- package/dist/analyzer/analyzeStyle.js.map +1 -0
- package/dist/analyzer/classifyValue.d.ts +7 -0
- package/dist/analyzer/classifyValue.d.ts.map +1 -0
- package/dist/analyzer/classifyValue.js +30 -0
- package/dist/analyzer/classifyValue.js.map +1 -0
- package/dist/analyzer/isThemeAccess.d.ts +7 -0
- package/dist/analyzer/isThemeAccess.d.ts.map +1 -0
- package/dist/analyzer/isThemeAccess.js +17 -0
- package/dist/analyzer/isThemeAccess.js.map +1 -0
- package/dist/cli/runTransform.d.ts +2 -0
- package/dist/cli/runTransform.d.ts.map +1 -0
- package/dist/cli/runTransform.js +55 -0
- package/dist/cli/runTransform.js.map +1 -0
- package/dist/config/createTheme.d.ts +101 -0
- package/dist/config/createTheme.d.ts.map +1 -0
- package/dist/config/createTheme.js +61 -0
- package/dist/config/createTheme.js.map +1 -0
- package/dist/config/generateTypes.d.ts +2 -0
- package/dist/config/generateTypes.d.ts.map +1 -0
- package/dist/config/generateTypes.js +103 -0
- package/dist/config/generateTypes.js.map +1 -0
- package/dist/config/loadStyledConfig.d.ts +13 -0
- package/dist/config/loadStyledConfig.d.ts.map +1 -0
- package/dist/config/loadStyledConfig.js +175 -0
- package/dist/config/loadStyledConfig.js.map +1 -0
- package/dist/context/Provider.d.ts +40 -0
- package/dist/context/Provider.d.ts.map +1 -0
- package/dist/context/Provider.js +141 -0
- package/dist/context/Provider.js.map +1 -0
- package/dist/core/shouldTransform.d.ts +6 -0
- package/dist/core/shouldTransform.d.ts.map +1 -0
- package/dist/core/shouldTransform.js +28 -0
- package/dist/core/shouldTransform.js.map +1 -0
- package/dist/detectors/isStyledCall.d.ts +10 -0
- package/dist/detectors/isStyledCall.d.ts.map +1 -0
- package/dist/detectors/isStyledCall.js +20 -0
- package/dist/detectors/isStyledCall.js.map +1 -0
- package/dist/detectors/isStyledImported.d.ts +9 -0
- package/dist/detectors/isStyledImported.d.ts.map +1 -0
- package/dist/detectors/isStyledImported.js +27 -0
- package/dist/detectors/isStyledImported.js.map +1 -0
- package/dist/extractors/extractAttrs.d.ts +13 -0
- package/dist/extractors/extractAttrs.d.ts.map +1 -0
- package/dist/extractors/extractAttrs.js +40 -0
- package/dist/extractors/extractAttrs.js.map +1 -0
- package/dist/extractors/extractStyle.d.ts +24 -0
- package/dist/extractors/extractStyle.d.ts.map +1 -0
- package/dist/extractors/extractStyle.js +57 -0
- package/dist/extractors/extractStyle.js.map +1 -0
- package/dist/extractors/extractVariants.d.ts +11 -0
- package/dist/extractors/extractVariants.d.ts.map +1 -0
- package/dist/extractors/extractVariants.js +31 -0
- package/dist/extractors/extractVariants.js.map +1 -0
- package/dist/extractors/getStyledConfig.d.ts +9 -0
- package/dist/extractors/getStyledConfig.d.ts.map +1 -0
- package/dist/extractors/getStyledConfig.js +17 -0
- package/dist/extractors/getStyledConfig.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/Styled.d.ts +48 -0
- package/dist/runtime/Styled.d.ts.map +1 -0
- package/dist/runtime/Styled.js +17 -0
- package/dist/runtime/Styled.js.map +1 -0
- package/dist/theme/astFromValue.d.ts +3 -0
- package/dist/theme/astFromValue.d.ts.map +1 -0
- package/dist/theme/astFromValue.js +22 -0
- package/dist/theme/astFromValue.js.map +1 -0
- package/dist/theme/extractThemePath.d.ts +7 -0
- package/dist/theme/extractThemePath.d.ts.map +1 -0
- package/dist/theme/extractThemePath.js +21 -0
- package/dist/theme/extractThemePath.js.map +1 -0
- package/dist/theme/resolveExpression.d.ts +7 -0
- package/dist/theme/resolveExpression.d.ts.map +1 -0
- package/dist/theme/resolveExpression.js +46 -0
- package/dist/theme/resolveExpression.js.map +1 -0
- package/dist/theme/resolveThemeInStyle.d.ts +6 -0
- package/dist/theme/resolveThemeInStyle.d.ts.map +1 -0
- package/dist/theme/resolveThemeInStyle.js +26 -0
- package/dist/theme/resolveThemeInStyle.js.map +1 -0
- package/dist/theme/resolveThemePath.d.ts +8 -0
- package/dist/theme/resolveThemePath.d.ts.map +1 -0
- package/dist/theme/resolveThemePath.js +22 -0
- package/dist/theme/resolveThemePath.js.map +1 -0
- package/dist/transformers/transformStyled.d.ts +7 -0
- package/dist/transformers/transformStyled.d.ts.map +1 -0
- package/dist/transformers/transformStyled.js +104 -0
- package/dist/transformers/transformStyled.js.map +1 -0
- package/dist/transformers/transformStyledWithVariants.d.ts +22 -0
- package/dist/transformers/transformStyledWithVariants.d.ts.map +1 -0
- package/dist/transformers/transformStyledWithVariants.js +197 -0
- package/dist/transformers/transformStyledWithVariants.js.map +1 -0
- package/dist/utils/buildDefaultVariantsAST.d.ts +3 -0
- package/dist/utils/buildDefaultVariantsAST.d.ts.map +1 -0
- package/dist/utils/buildDefaultVariantsAST.js +41 -0
- package/dist/utils/buildDefaultVariantsAST.js.map +1 -0
- package/dist/utils/buildVariantsObjectAST.d.ts +3 -0
- package/dist/utils/buildVariantsObjectAST.d.ts.map +1 -0
- package/dist/utils/buildVariantsObjectAST.js +43 -0
- package/dist/utils/buildVariantsObjectAST.js.map +1 -0
- package/dist/utils/defaultFontStyle.d.ts +8 -0
- package/dist/utils/defaultFontStyle.d.ts.map +1 -0
- package/dist/utils/defaultFontStyle.js +32 -0
- package/dist/utils/defaultFontStyle.js.map +1 -0
- package/dist/utils/ensureImport.d.ts +13 -0
- package/dist/utils/ensureImport.d.ts.map +1 -0
- package/dist/utils/ensureImport.js +76 -0
- package/dist/utils/ensureImport.js.map +1 -0
- package/dist/utils/getProgramPath.d.ts +6 -0
- package/dist/utils/getProgramPath.d.ts.map +1 -0
- package/dist/utils/getProgramPath.js +10 -0
- package/dist/utils/getProgramPath.js.map +1 -0
- package/dist/utils/nodeHasPlatformSelect.d.ts +3 -0
- package/dist/utils/nodeHasPlatformSelect.d.ts.map +1 -0
- package/dist/utils/nodeHasPlatformSelect.js +37 -0
- package/dist/utils/nodeHasPlatformSelect.js.map +1 -0
- package/dist/utils/nodeHasThemeAccess.d.ts +7 -0
- package/dist/utils/nodeHasThemeAccess.d.ts.map +1 -0
- package/dist/utils/nodeHasThemeAccess.js +36 -0
- package/dist/utils/nodeHasThemeAccess.js.map +1 -0
- package/dist/utils/replaceParamWithProps.d.ts +7 -0
- package/dist/utils/replaceParamWithProps.d.ts.map +1 -0
- package/dist/utils/replaceParamWithProps.js +34 -0
- package/dist/utils/replaceParamWithProps.js.map +1 -0
- package/package.json +95 -0
package/README.md
ADDED
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
# stampd
|
|
2
|
+
|
|
3
|
+
React Native styling, stamped at compile time.
|
|
4
|
+
|
|
5
|
+
Design tokens resolved to literal values in your bundle — zero runtime overhead, full TypeScript, light / dark / high-contrast built in.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
const Card = Styled.View({
|
|
9
|
+
style: ({ theme }) => ({
|
|
10
|
+
backgroundColor: theme.colors.surface,
|
|
11
|
+
padding: theme.spacing.md, // → 16
|
|
12
|
+
borderRadius: theme.radius.md, // → 8
|
|
13
|
+
}),
|
|
14
|
+
});
|
|
15
|
+
// ↓ Babel output
|
|
16
|
+
const Card = (props) => (
|
|
17
|
+
<View style={{ backgroundColor: theme.colors.surface, padding: 16, borderRadius: 8 }} {...props} />
|
|
18
|
+
);
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Table of Contents
|
|
24
|
+
|
|
25
|
+
- [Quick Start](#quick-start)
|
|
26
|
+
- [How it works](#how-it-works)
|
|
27
|
+
- [Installation](#installation)
|
|
28
|
+
- [Setup](#setup)
|
|
29
|
+
- [1. babel.config.js](#1-babelconfigjs)
|
|
30
|
+
- [2. stampd.config.ts](#2-stampdconfigts)
|
|
31
|
+
- [3. tsconfig.json](#3-tsconfigjson)
|
|
32
|
+
- [4. StampdUIProvider](#4-wrap-your-app-with-stampdprovider)
|
|
33
|
+
- [Usage](#usage)
|
|
34
|
+
- [Simple component](#simple-component)
|
|
35
|
+
- [Static component](#static-component-no-runtime-cost)
|
|
36
|
+
- [Text with default font](#text-with-default-font-auto-injected)
|
|
37
|
+
- [Variants](#variants)
|
|
38
|
+
- [Dynamic props](#dynamic-props)
|
|
39
|
+
- [attrs — default props](#attrs--default-props)
|
|
40
|
+
- [theme.fonts](#using-themefont)
|
|
41
|
+
- [StampdUIProvider & useStampdUI](#stampdprovider--usestampd)
|
|
42
|
+
- [Switching themes](#switching-themes)
|
|
43
|
+
- [High contrast](#high-contrast)
|
|
44
|
+
- [Font scale](#font-scale)
|
|
45
|
+
- [Accessing the theme](#accessing-the-theme-directly)
|
|
46
|
+
- [createTheme API](#createtheme-api)
|
|
47
|
+
- [Style priority](#style-priority)
|
|
48
|
+
- [Generated types](#generated-types--stampd-typesd-ts)
|
|
49
|
+
- [CLI — inspect output](#cli--inspect-generated-output)
|
|
50
|
+
- [Supported components](#supported-components)
|
|
51
|
+
- [License](#license)
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm install stampd
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**1.** Add the plugin to `babel.config.js`:
|
|
62
|
+
```js
|
|
63
|
+
module.exports = {
|
|
64
|
+
presets: ['babel-preset-expo'],
|
|
65
|
+
plugins: ['module:stampd'],
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**2.** Create `stampd.config.ts` at your project root:
|
|
70
|
+
```ts
|
|
71
|
+
import { createTheme } from 'stampd/theme';
|
|
72
|
+
|
|
73
|
+
const light = { primary: '#2563EB', background: '#F8FAFC', text: '#0F172A' };
|
|
74
|
+
const dark = { primary: '#3B82F6', background: '#0F172A', text: '#F8FAFC' };
|
|
75
|
+
|
|
76
|
+
export const config = createTheme({
|
|
77
|
+
tokens: {
|
|
78
|
+
spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },
|
|
79
|
+
radius: { sm: 4, md: 8, lg: 16, full: 9999 },
|
|
80
|
+
},
|
|
81
|
+
theme: {
|
|
82
|
+
light: { colors: light },
|
|
83
|
+
dark: { colors: dark },
|
|
84
|
+
},
|
|
85
|
+
fonts: {
|
|
86
|
+
default: { size: 14, family: 'Inter' },
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**3.** Add `stampd-types.d.ts` to `tsconfig.json`:
|
|
92
|
+
```json
|
|
93
|
+
{ "include": ["**/*.ts", "**/*.tsx", "stampd-types.d.ts"] }
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**4.** Wrap your app:
|
|
97
|
+
```tsx
|
|
98
|
+
import { StampdUIProvider } from 'stampd/context';
|
|
99
|
+
import { config } from './stampd.config';
|
|
100
|
+
|
|
101
|
+
export default function App() {
|
|
102
|
+
return <StampdUIProvider config={config}><RootNavigator /></StampdUIProvider>;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**5.** Start building:
|
|
107
|
+
```tsx
|
|
108
|
+
import { Styled } from 'stampd/styled';
|
|
109
|
+
|
|
110
|
+
const Button = Styled.TouchableOpacity({
|
|
111
|
+
style: ({ theme }) => ({
|
|
112
|
+
backgroundColor: theme.colors.primary, // runtime — light/dark aware
|
|
113
|
+
padding: theme.spacing.md, // compile time → 16
|
|
114
|
+
borderRadius: theme.radius.sm, // compile time → 4
|
|
115
|
+
}),
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## How it works
|
|
122
|
+
|
|
123
|
+
stampd runs as a **Babel plugin** during your build:
|
|
124
|
+
|
|
125
|
+
1. Reads `stampd.config.ts` from your project root at compile time.
|
|
126
|
+
2. Resolves every `theme.spacing.md`, `theme.radius.sm`, `theme.fonts.sizes.lg`, etc. to its **literal value** and hardcodes it in the bundle.
|
|
127
|
+
3. Colors and other values that differ between light / dark are kept as runtime references — `useStampdUI()` is injected **only when actually needed**.
|
|
128
|
+
4. Variants are hoisted to module-level constants, allocated once.
|
|
129
|
+
5. Generates `stampd-types.d.ts` with inline literal types for full IDE autocomplete.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Installation
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npm install stampd
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Setup
|
|
142
|
+
|
|
143
|
+
### 1. babel.config.js
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
module.exports = function (api) {
|
|
147
|
+
// Re-run Babel when stampd.config.ts changes
|
|
148
|
+
const configPath = require('path').join(__dirname, 'stampd.config.ts');
|
|
149
|
+
try {
|
|
150
|
+
api.cache.using(() => require('fs').statSync(configPath).mtimeMs);
|
|
151
|
+
} catch {
|
|
152
|
+
api.cache(true);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
presets: ['babel-preset-expo'],
|
|
157
|
+
plugins: ['module:stampd'],
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Enable debug output to inspect generated code:
|
|
163
|
+
|
|
164
|
+
```js
|
|
165
|
+
plugins: [['module:stampd', { debug: true }]],
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### 2. stampd.config.ts
|
|
171
|
+
|
|
172
|
+
Create `stampd.config.ts` at the **root of your project** (next to `package.json`):
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { createTheme } from 'stampd/theme';
|
|
176
|
+
|
|
177
|
+
const lightColors = {
|
|
178
|
+
primary: '#2563EB',
|
|
179
|
+
background: '#F8FAFC',
|
|
180
|
+
surface: '#FFFFFF',
|
|
181
|
+
text: '#0F172A',
|
|
182
|
+
error: '#DC2626',
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const darkColors = {
|
|
186
|
+
primary: '#3B82F6',
|
|
187
|
+
background: '#0F172A',
|
|
188
|
+
surface: '#1E293B',
|
|
189
|
+
text: '#F8FAFC',
|
|
190
|
+
error: '#EF4444',
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export const config = createTheme({
|
|
194
|
+
// Resolved at compile time — hardcoded in the bundle
|
|
195
|
+
tokens: {
|
|
196
|
+
spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },
|
|
197
|
+
radius: { sm: 4, md: 8, lg: 16, full: 9999 },
|
|
198
|
+
fontSize: { sm: 12, md: 14, lg: 16, xl: 20, xxl: 24 },
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// Resolved at runtime — switch between light / dark / high-contrast
|
|
202
|
+
theme: {
|
|
203
|
+
light: { colors: lightColors },
|
|
204
|
+
dark: { colors: darkColors },
|
|
205
|
+
highContrast: { colors: { background: '#000000', text: '#FFFFFF' } }, // partial overrides
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
// Static font config — auto-injected into Text / TextInput
|
|
209
|
+
fonts: {
|
|
210
|
+
default: { size: 14, family: 'Inter' },
|
|
211
|
+
sizes: { sm: 12, md: 14, lg: 16, xl: 20 },
|
|
212
|
+
family: { inter: 'Inter', mono: 'JetBrainsMono' },
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`createTheme` validates at startup that `dark` has the same keys as `light`, warning if they diverge.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### 3. tsconfig.json
|
|
222
|
+
|
|
223
|
+
Include the auto-generated type file:
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"include": ["**/*.ts", "**/*.tsx", "stampd-types.d.ts"]
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### 4. Wrap your app with StampdUIProvider
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
// App.tsx
|
|
237
|
+
import { StampdUIProvider } from 'stampd/context';
|
|
238
|
+
import { config } from './stampd.config';
|
|
239
|
+
|
|
240
|
+
export default function App() {
|
|
241
|
+
return (
|
|
242
|
+
<StampdUIProvider config={config}>
|
|
243
|
+
<RootNavigator />
|
|
244
|
+
</StampdUIProvider>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Usage
|
|
252
|
+
|
|
253
|
+
### Simple component
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
import { Styled } from 'stampd';
|
|
257
|
+
|
|
258
|
+
const Card = Styled.View({
|
|
259
|
+
style: ({ theme }) => ({
|
|
260
|
+
backgroundColor: theme.colors.surface, // runtime — stays as ref
|
|
261
|
+
padding: theme.spacing.md, // compile time → 16
|
|
262
|
+
borderRadius: theme.radius.md, // compile time → 8
|
|
263
|
+
}),
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Compile-time output:**
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
270
|
+
import { View } from 'react-native';
|
|
271
|
+
|
|
272
|
+
const Card = (props) => (
|
|
273
|
+
<View
|
|
274
|
+
style={[{ backgroundColor: theme.colors.surface }, { padding: 16, borderRadius: 8 }]}
|
|
275
|
+
{...props}
|
|
276
|
+
/>
|
|
277
|
+
);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
### Static component (no runtime cost)
|
|
283
|
+
|
|
284
|
+
When there are no dynamic values, the output is a plain component — no context, no hook:
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
const Divider = Styled.View({
|
|
288
|
+
style: {
|
|
289
|
+
height: 1,
|
|
290
|
+
backgroundColor: '#E2E8F0',
|
|
291
|
+
marginVertical: 8,
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
### Text with default font auto-injected
|
|
299
|
+
|
|
300
|
+
`Styled.Text` and `Styled.TextInput` automatically receive `fontFamily` and `fontSize` from `fonts.default` as the base layer. Your component styles take precedence:
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
const Label = Styled.Text({
|
|
304
|
+
style: ({ theme }) => ({
|
|
305
|
+
color: theme.colors.text,
|
|
306
|
+
fontWeight: '600',
|
|
307
|
+
}),
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Output:**
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
<Text
|
|
315
|
+
style={[
|
|
316
|
+
{ fontFamily: 'Inter', fontSize: 14 }, // ← auto from fonts.default
|
|
317
|
+
{ color: theme.colors.text, fontWeight: '600' },
|
|
318
|
+
]}
|
|
319
|
+
{...props}
|
|
320
|
+
/>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Override per-component by setting `fontFamily` / `fontSize` in your style — the last item wins.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
### Variants
|
|
328
|
+
|
|
329
|
+
```tsx
|
|
330
|
+
const Button = Styled.TouchableOpacity({
|
|
331
|
+
style: ({ theme }) => ({
|
|
332
|
+
backgroundColor: theme.colors.primary,
|
|
333
|
+
paddingVertical: theme.spacing.sm,
|
|
334
|
+
paddingHorizontal: theme.spacing.md,
|
|
335
|
+
borderRadius: theme.radius.md,
|
|
336
|
+
alignItems: 'center',
|
|
337
|
+
}),
|
|
338
|
+
variants: {
|
|
339
|
+
variant: {
|
|
340
|
+
outline: ({ theme }) => ({
|
|
341
|
+
backgroundColor: 'transparent',
|
|
342
|
+
borderWidth: 1,
|
|
343
|
+
borderColor: theme.colors.primary,
|
|
344
|
+
}),
|
|
345
|
+
ghost: () => ({
|
|
346
|
+
backgroundColor: 'transparent',
|
|
347
|
+
}),
|
|
348
|
+
danger: ({ theme }) => ({
|
|
349
|
+
backgroundColor: theme.colors.error,
|
|
350
|
+
}),
|
|
351
|
+
},
|
|
352
|
+
size: {
|
|
353
|
+
sm: ({ theme }) => ({
|
|
354
|
+
paddingVertical: theme.spacing.xs,
|
|
355
|
+
paddingHorizontal: theme.spacing.sm,
|
|
356
|
+
}),
|
|
357
|
+
full: () => ({ width: '100%' as const }),
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Usage — types are fully inferred:
|
|
363
|
+
<Button variant="outline" size="sm" onPress={handlePress} />
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Passing an invalid variant value (`variant="invalid"`) is a TypeScript error.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
### Dynamic props
|
|
371
|
+
|
|
372
|
+
Any prop that is not a variant key is automatically forwarded and available in the style function:
|
|
373
|
+
|
|
374
|
+
```tsx
|
|
375
|
+
const Tag = Styled.View({
|
|
376
|
+
style: ({ theme, selected }) => ({
|
|
377
|
+
backgroundColor: selected ? theme.colors.primary : theme.colors.surface,
|
|
378
|
+
borderWidth: selected ? 0 : 1,
|
|
379
|
+
paddingHorizontal: theme.spacing.sm,
|
|
380
|
+
borderRadius: theme.radius.full,
|
|
381
|
+
}),
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
<Tag selected={isActive} />
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
### attrs — default props
|
|
390
|
+
|
|
391
|
+
```tsx
|
|
392
|
+
const Input = Styled.TextInput({
|
|
393
|
+
style: ({ theme }) => ({
|
|
394
|
+
color: theme.colors.text,
|
|
395
|
+
padding: theme.spacing.sm,
|
|
396
|
+
}),
|
|
397
|
+
attrs: {
|
|
398
|
+
placeholderTextColor: '#94A3B8',
|
|
399
|
+
autoCapitalize: 'none',
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
### Using theme.fonts
|
|
407
|
+
|
|
408
|
+
```tsx
|
|
409
|
+
const Heading = Styled.Text({
|
|
410
|
+
style: ({ theme }) => ({
|
|
411
|
+
fontFamily: theme.fonts.family.inter, // compile time → "Inter"
|
|
412
|
+
fontSize: theme.fonts.sizes.xl, // compile time → 20
|
|
413
|
+
fontWeight: '700',
|
|
414
|
+
color: theme.colors.text,
|
|
415
|
+
}),
|
|
416
|
+
});
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
`theme.fonts.*` is resolved at compile time — literal values are hardcoded in the output.
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## StampdUIProvider & useStampdUI
|
|
424
|
+
|
|
425
|
+
### Switching themes
|
|
426
|
+
|
|
427
|
+
```tsx
|
|
428
|
+
import { useStampdUI, ThemeMode } from 'stampd/context';
|
|
429
|
+
|
|
430
|
+
function ThemeToggle() {
|
|
431
|
+
const { themeMode, setThemeMode } = useStampdUI();
|
|
432
|
+
|
|
433
|
+
return (
|
|
434
|
+
<Pressable onPress={() =>
|
|
435
|
+
setThemeMode(themeMode === ThemeMode.DARK ? ThemeMode.LIGHT : ThemeMode.DARK)
|
|
436
|
+
}>
|
|
437
|
+
<Text>Toggle theme</Text>
|
|
438
|
+
</Pressable>
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
| `ThemeMode` | Behavior |
|
|
444
|
+
|------------------------|------------------------------------|
|
|
445
|
+
| `ThemeMode.SYSTEM` | Follows device setting *(default)* |
|
|
446
|
+
| `ThemeMode.LIGHT` | Always light |
|
|
447
|
+
| `ThemeMode.DARK` | Always dark |
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
### High contrast
|
|
452
|
+
|
|
453
|
+
```tsx
|
|
454
|
+
const { highContrast, setHighContrast } = useStampdUI();
|
|
455
|
+
|
|
456
|
+
<Switch value={highContrast} onValueChange={setHighContrast} />
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
When enabled, the `highContrast` colors from `stampd.config.ts` are **deep-merged** on top of the active light / dark theme. Only the keys you define are overridden — everything else stays unchanged.
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
### Font scale
|
|
464
|
+
|
|
465
|
+
```tsx
|
|
466
|
+
import { useStampdUI, FontScaleMode } from 'stampd/context';
|
|
467
|
+
|
|
468
|
+
const { fontScale, fontScaleMode, setFontScaleMode } = useStampdUI();
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
| `FontScaleMode` | Behavior |
|
|
472
|
+
|---------------------------|-----------------------------------------------|
|
|
473
|
+
| `FontScaleMode.SYSTEM` | Follows device accessibility setting *(default)* |
|
|
474
|
+
| `FontScaleMode.FIXED_1` | Always 1× |
|
|
475
|
+
| `FontScaleMode.FIXED_1_5` | Always 1.5× |
|
|
476
|
+
| `FontScaleMode.FIXED_2` | Always 2× |
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
### Accessing the theme directly
|
|
481
|
+
|
|
482
|
+
```tsx
|
|
483
|
+
const { theme } = useStampdUI();
|
|
484
|
+
|
|
485
|
+
// theme.colors.primary, theme.spacing.md, theme.fonts.family.inter, ...
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## createTheme API
|
|
491
|
+
|
|
492
|
+
```ts
|
|
493
|
+
import { createTheme, InferTheme } from 'stampd/theme';
|
|
494
|
+
|
|
495
|
+
export const config = createTheme({
|
|
496
|
+
// ── Compile-time tokens ──────────────────────────────────────────────────
|
|
497
|
+
tokens: {
|
|
498
|
+
spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },
|
|
499
|
+
radius: { sm: 4, md: 8, lg: 16, full: 9999 },
|
|
500
|
+
fontSize: { sm: 12, md: 14, lg: 16, xl: 20, xxl: 24 },
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
// ── Runtime theme ────────────────────────────────────────────────────────
|
|
504
|
+
theme: {
|
|
505
|
+
light: { colors: lightColors },
|
|
506
|
+
dark: { colors: darkColors },
|
|
507
|
+
highContrast: { colors: { background: '#000' } }, // DeepPartial — only overrides what's set
|
|
508
|
+
},
|
|
509
|
+
|
|
510
|
+
// ── Fonts ────────────────────────────────────────────────────────────────
|
|
511
|
+
fonts: {
|
|
512
|
+
default: { size: 14, family: 'Inter' }, // auto-injected in Text / TextInput
|
|
513
|
+
sizes: { sm: 12, md: 14, lg: 16, xl: 20 },
|
|
514
|
+
family: { inter: 'Inter', mono: 'JetBrainsMono' },
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
// Infer the resolved theme type:
|
|
519
|
+
export type AppTheme = InferTheme<typeof config>;
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## Style priority
|
|
525
|
+
|
|
526
|
+
When multiple layers are applied, the **last item wins** (React Native style array behavior):
|
|
527
|
+
|
|
528
|
+
```
|
|
529
|
+
fonts.default < base style < variants < style prop passed by user
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
The user's `style` prop always wins over everything defined at component level.
|
|
533
|
+
|
|
534
|
+
---
|
|
535
|
+
|
|
536
|
+
## Generated types — stampd-types.d.ts
|
|
537
|
+
|
|
538
|
+
On every build, stampd generates `stampd-types.d.ts` at your project root with **inline literal types** for full IDE autocomplete — no imports needed in your components:
|
|
539
|
+
|
|
540
|
+
```ts
|
|
541
|
+
// ⚡ Auto-generated by stampd — do not edit manually.
|
|
542
|
+
declare global {
|
|
543
|
+
namespace StyledSystem {
|
|
544
|
+
interface Theme {
|
|
545
|
+
spacing: { xs: 4; sm: 8; md: 16; lg: 24; xl: 32 };
|
|
546
|
+
radius: { sm: 4; md: 8; lg: 16; full: 9999 };
|
|
547
|
+
fontSize: { sm: 12; md: 14; lg: 16; xl: 20; xxl: 24 };
|
|
548
|
+
colors: { primary: "#2563EB"; background: "#F8FAFC"; ... };
|
|
549
|
+
fonts: {
|
|
550
|
+
default: { size: 14; family: "Inter" };
|
|
551
|
+
sizes: { sm: 12; md: 14; lg: 16; xl: 20 };
|
|
552
|
+
family: { inter: "Inter"; mono: "JetBrainsMono" };
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
The file is regenerated automatically when `stampd.config.ts` changes.
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## CLI — inspect generated output
|
|
564
|
+
|
|
565
|
+
Inspect exactly what the plugin emits for any file without running the full bundler:
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
node node_modules/stampd/dist/cli/runTransform.js <input> <output> [config]
|
|
569
|
+
|
|
570
|
+
# Example:
|
|
571
|
+
node node_modules/stampd/dist/cli/runTransform.js \
|
|
572
|
+
src/screens/Login.tsx \
|
|
573
|
+
/tmp/Login.out.js \
|
|
574
|
+
stampd.config.ts
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
Add as a script in `package.json`:
|
|
578
|
+
|
|
579
|
+
```json
|
|
580
|
+
{
|
|
581
|
+
"scripts": {
|
|
582
|
+
"transform": "node node_modules/stampd/dist/cli/runTransform.js"
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
## Supported components
|
|
590
|
+
|
|
591
|
+
`Styled` ships with typed factories for all standard React Native components:
|
|
592
|
+
|
|
593
|
+
| Component | Style type |
|
|
594
|
+
|---------------------------------|---------------|
|
|
595
|
+
| `Styled.View` | `ViewStyle` |
|
|
596
|
+
| `Styled.Text` | `TextStyle` |
|
|
597
|
+
| `Styled.TextInput` | `TextStyle` |
|
|
598
|
+
| `Styled.Image` | `ImageStyle` |
|
|
599
|
+
| `Styled.ImageBackground` | `ViewStyle` |
|
|
600
|
+
| `Styled.ScrollView` | `ViewStyle` |
|
|
601
|
+
| `Styled.Pressable` | `ViewStyle` |
|
|
602
|
+
| `Styled.TouchableOpacity` | `ViewStyle` |
|
|
603
|
+
| `Styled.KeyboardAvoidingView` | `ViewStyle` |
|
|
604
|
+
| `Styled.FlatList` | `ViewStyle` |
|
|
605
|
+
| `Styled.SectionList` | `ViewStyle` |
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## License
|
|
610
|
+
|
|
611
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { types as t } from "@babel/core";
|
|
2
|
+
export type StyleAnalysis = {
|
|
3
|
+
type: "SAFE";
|
|
4
|
+
} | {
|
|
5
|
+
type: "PARTIAL";
|
|
6
|
+
} | {
|
|
7
|
+
type: "UNSAFE";
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Analisa o objeto de style inteiro
|
|
11
|
+
*/
|
|
12
|
+
export declare function analyzeStyle(style: t.ObjectExpression): StyleAnalysis;
|
|
13
|
+
//# sourceMappingURL=analyzeStyle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzeStyle.d.ts","sourceRoot":"","sources":["../../src/analyzer/analyzeStyle.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,CAAC,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEvB;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC,gBAAgB,GAAG,aAAa,CAqCrE"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyzeStyle = analyzeStyle;
|
|
4
|
+
// plugins/src/analyzer/analyzeStyle.ts
|
|
5
|
+
const core_1 = require("@babel/core");
|
|
6
|
+
const classifyValue_1 = require("./classifyValue");
|
|
7
|
+
/**
|
|
8
|
+
* Analisa o objeto de style inteiro
|
|
9
|
+
*/
|
|
10
|
+
function analyzeStyle(style) {
|
|
11
|
+
let hasDynamic = false;
|
|
12
|
+
let hasStaticOrTheme = false;
|
|
13
|
+
for (const prop of style.properties) {
|
|
14
|
+
if (core_1.types.isSpreadElement(prop)) {
|
|
15
|
+
const type = (0, classifyValue_1.classifyValue)(prop);
|
|
16
|
+
if (type === "DYNAMIC")
|
|
17
|
+
hasDynamic = true;
|
|
18
|
+
else
|
|
19
|
+
hasStaticOrTheme = true;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (!core_1.types.isObjectProperty(prop))
|
|
23
|
+
continue;
|
|
24
|
+
const valueType = (0, classifyValue_1.classifyValue)(prop.value);
|
|
25
|
+
if (valueType === "DYNAMIC") {
|
|
26
|
+
hasDynamic = true;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
hasStaticOrTheme = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// 🔴 tudo dinâmico → não mexe
|
|
33
|
+
if (hasDynamic && !hasStaticOrTheme) {
|
|
34
|
+
return { type: "UNSAFE" };
|
|
35
|
+
}
|
|
36
|
+
// 🟡 misto → parcial
|
|
37
|
+
if (hasDynamic && hasStaticOrTheme) {
|
|
38
|
+
return { type: "PARTIAL" };
|
|
39
|
+
}
|
|
40
|
+
// 🟢 tudo seguro → pode hardcode
|
|
41
|
+
return { type: "SAFE" };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=analyzeStyle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzeStyle.js","sourceRoot":"","sources":["../../src/analyzer/analyzeStyle.ts"],"names":[],"mappings":";;AAYA,oCAqCC;AAjDD,uCAAuC;AACvC,sCAAyC;AACzC,mDAA2D;AAO3D;;GAEG;AACH,SAAgB,YAAY,CAAC,KAAyB;IACpD,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAE7B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACpC,IAAI,YAAC,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAA,6BAAa,EAAC,IAAI,CAAC,CAAC;YAEjC,IAAI,IAAI,KAAK,SAAS;gBAAE,UAAU,GAAG,IAAI,CAAC;;gBACrC,gBAAgB,GAAG,IAAI,CAAC;YAE7B,SAAS;QACX,CAAC;QAED,IAAI,CAAC,YAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC;YAAE,SAAS;QAExC,MAAM,SAAS,GAAG,IAAA,6BAAa,EAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAE5C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,gBAAgB,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,8BAA8B;IAC9B,IAAI,UAAU,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACpC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC5B,CAAC;IAED,qBAAqB;IACrB,IAAI,UAAU,IAAI,gBAAgB,EAAE,CAAC;QACnC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC7B,CAAC;IAED,iCAAiC;IACjC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifyValue.d.ts","sourceRoot":"","sources":["../../src/analyzer/classifyValue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,CAAC,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvD;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,GAAG,SAAS,CAwBrD"}
|