@cupped/tokens 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.
@@ -0,0 +1,279 @@
1
+ // GENERATED FILE — do not edit. Canonical source: tokens/**/*.tokens.json
2
+ // (npm run build in @cupped/tokens). Dimensions are unitless RN dp numbers,
3
+ // durations are ms, fontFamily is a single string. shadow.*.boxShadow targets
4
+ // RN 0.76+/new architecture; shadow.*.ios/android are the legacy props.
5
+ export const tokens = {
6
+ "color": {
7
+ "canvas": "#F8FAFC",
8
+ "card": "#FFFFFF",
9
+ "canvasBorder": "#E2E8F0",
10
+ "canvasBorderSubtle": "#F1F5F9",
11
+ "ink": {
12
+ "default": "#0F172A",
13
+ "secondary": "#475569",
14
+ "muted": "#94A3B8",
15
+ "inverse": "#F8FAFC"
16
+ },
17
+ "primary": {
18
+ "default": "#E07A5F",
19
+ "strong": "#C05539",
20
+ "strongHover": "#AD4A30",
21
+ "hover": "#D16A4F",
22
+ "light": "#FDF2F0",
23
+ "muted": "rgba(224, 122, 95, 0.12)"
24
+ },
25
+ "success": {
26
+ "default": "#22C55E",
27
+ "light": "#F0FDF4",
28
+ "ink": "#15803D"
29
+ },
30
+ "error": {
31
+ "default": "#EF4444",
32
+ "light": "#FEF2F2",
33
+ "ink": "#B91C1C"
34
+ },
35
+ "warning": {
36
+ "default": "#F59E0B",
37
+ "light": "#FFFBEB",
38
+ "ink": "#B45309"
39
+ },
40
+ "info": {
41
+ "default": "#3B82F6",
42
+ "light": "#EFF6FF",
43
+ "ink": "#1D4ED8"
44
+ },
45
+ "flavor": {
46
+ "fruity": {
47
+ "fill": "#B44C54",
48
+ "text": "#9B494E"
49
+ },
50
+ "berry": {
51
+ "fill": "#AC4D7B",
52
+ "text": "#94496D"
53
+ },
54
+ "citrus": {
55
+ "fill": "#A65F02",
56
+ "text": "#925512"
57
+ },
58
+ "sweet": {
59
+ "fill": "#8E6E09",
60
+ "text": "#7E6207"
61
+ },
62
+ "floral": {
63
+ "fill": "#865CB2",
64
+ "text": "#755498"
65
+ },
66
+ "green": {
67
+ "fill": "#4E8429",
68
+ "text": "#49742E"
69
+ },
70
+ "spice": {
71
+ "fill": "#B4503A",
72
+ "text": "#9B4C3A"
73
+ },
74
+ "nutty": {
75
+ "fill": "#876F49",
76
+ "text": "#79623E"
77
+ },
78
+ "chocolate": {
79
+ "fill": "#916951",
80
+ "text": "#825C46"
81
+ },
82
+ "roasted": {
83
+ "fill": "#896B5E",
84
+ "text": "#7B5F52"
85
+ }
86
+ },
87
+ "onFlavor": "#FFFFFF",
88
+ "xp": "#8E6E09",
89
+ "xpAccessible": "#7E6207",
90
+ "ember": "#F97316",
91
+ "emberAccessible": "#C2410C",
92
+ "streak": "#F97316",
93
+ "streakAccessible": "#C2410C",
94
+ "badge": "#865CB2",
95
+ "badgeAccessible": "#755498",
96
+ "zone": {
97
+ "ideal": "#22C55E",
98
+ "under": "#3B82F6",
99
+ "over": "#EF4444",
100
+ "strong": "#F59E0B",
101
+ "weak": "#94A3B8"
102
+ },
103
+ "chart": {
104
+ "sca": "#E07A5F",
105
+ "ucdavis": "#865CB2",
106
+ "grid": "#E2E8F0"
107
+ }
108
+ },
109
+ "space": {
110
+ "xs": 4,
111
+ "sm": 8,
112
+ "md": 12,
113
+ "base": 16,
114
+ "lg": 20,
115
+ "xl": 24,
116
+ "xxl": 32,
117
+ "hitTargetMin": 44
118
+ },
119
+ "radius": {
120
+ "sm": 8,
121
+ "md": 12,
122
+ "lg": 16,
123
+ "xl": 24,
124
+ "full": 9999
125
+ },
126
+ "font": {
127
+ "sans": "Inter",
128
+ "serif": "Instrument Serif",
129
+ "mono": "JetBrains Mono"
130
+ },
131
+ "type": {
132
+ "screenTitle": {
133
+ "fontFamily": "Inter",
134
+ "fontSize": 32,
135
+ "fontWeight": "700",
136
+ "lineHeight": 35
137
+ },
138
+ "sheetTitle": {
139
+ "fontFamily": "Inter",
140
+ "fontSize": 24,
141
+ "fontWeight": "600",
142
+ "lineHeight": 29
143
+ },
144
+ "cardTitle": {
145
+ "fontFamily": "Inter",
146
+ "fontSize": 20,
147
+ "fontWeight": "600",
148
+ "lineHeight": 25
149
+ },
150
+ "body": {
151
+ "fontFamily": "Inter",
152
+ "fontSize": 16,
153
+ "fontWeight": "400",
154
+ "lineHeight": 24
155
+ },
156
+ "bodySm": {
157
+ "fontFamily": "Inter",
158
+ "fontSize": 14,
159
+ "fontWeight": "400",
160
+ "lineHeight": 20
161
+ },
162
+ "caption": {
163
+ "fontFamily": "Inter",
164
+ "fontSize": 12,
165
+ "fontWeight": "500",
166
+ "lineHeight": 17
167
+ },
168
+ "display": {
169
+ "fontFamily": "Instrument Serif",
170
+ "fontSize": 56,
171
+ "fontWeight": "400",
172
+ "lineHeight": 56,
173
+ "letterSpacing": -0.84
174
+ },
175
+ "mono": {
176
+ "fontFamily": "JetBrains Mono",
177
+ "fontSize": 12,
178
+ "fontWeight": "500",
179
+ "lineHeight": 17
180
+ }
181
+ },
182
+ "shadow": {
183
+ "sm": {
184
+ "boxShadow": "0 1px 2px rgba(15,23,42,0.04), 0 1px 1px rgba(15,23,42,0.03)",
185
+ "ios": {
186
+ "shadowColor": "#0F172A",
187
+ "shadowOffset": {
188
+ "width": 0,
189
+ "height": 1
190
+ },
191
+ "shadowOpacity": 0.04,
192
+ "shadowRadius": 2
193
+ },
194
+ "android": {
195
+ "elevation": 1
196
+ }
197
+ },
198
+ "md": {
199
+ "boxShadow": "0 4px 12px rgba(15,23,42,0.06), 0 1px 3px rgba(15,23,42,0.04)",
200
+ "ios": {
201
+ "shadowColor": "#0F172A",
202
+ "shadowOffset": {
203
+ "width": 0,
204
+ "height": 4
205
+ },
206
+ "shadowOpacity": 0.06,
207
+ "shadowRadius": 12
208
+ },
209
+ "android": {
210
+ "elevation": 4
211
+ }
212
+ },
213
+ "lg": {
214
+ "boxShadow": "0 12px 32px rgba(15,23,42,0.08), 0 4px 8px rgba(15,23,42,0.04)",
215
+ "ios": {
216
+ "shadowColor": "#0F172A",
217
+ "shadowOffset": {
218
+ "width": 0,
219
+ "height": 12
220
+ },
221
+ "shadowOpacity": 0.08,
222
+ "shadowRadius": 32
223
+ },
224
+ "android": {
225
+ "elevation": 8
226
+ }
227
+ },
228
+ "primary": {
229
+ "boxShadow": "0 12px 28px rgba(224,122,95,0.28), 0 4px 10px rgba(224,122,95,0.18)",
230
+ "ios": {
231
+ "shadowColor": "#E07A5F",
232
+ "shadowOffset": {
233
+ "width": 0,
234
+ "height": 12
235
+ },
236
+ "shadowOpacity": 0.28,
237
+ "shadowRadius": 28
238
+ },
239
+ "android": {
240
+ "elevation": 8,
241
+ "shadowColor": "#E07A5F"
242
+ }
243
+ }
244
+ },
245
+ "motion": {
246
+ "spring": {
247
+ "response": 550,
248
+ "damping": 0.75,
249
+ "reanimated": {
250
+ "stiffness": 130.51,
251
+ "damping": 17.14,
252
+ "mass": 1
253
+ },
254
+ "durationBased": {
255
+ "duration": 550,
256
+ "dampingRatio": 0.75
257
+ }
258
+ },
259
+ "tap": {
260
+ "scale": 0.95,
261
+ "opacity": 0.8
262
+ },
263
+ "staggerDelay": 80,
264
+ "shimmerDuration": 1500,
265
+ "reduced": 120
266
+ },
267
+ "focusRing": {
268
+ "innerWidth": 2,
269
+ "outerWidth": 2,
270
+ "innerColor": "#FFFFFF",
271
+ "color": "#C05539"
272
+ },
273
+ "material": {
274
+ "chromeBg": "rgba(255, 255, 255, 0.95)",
275
+ "scrim": "rgba(15, 23, 42, 0.45)",
276
+ "blur": 20,
277
+ "saturate": 1.4
278
+ }
279
+ };
@@ -0,0 +1,164 @@
1
+ /* ════════════════════════════════════════════════════════════════
2
+ CUPPED · TAILWIND v4 THEME
3
+ GENERATED FILE — do not edit. Canonical source: the DTCG token files
4
+ under tokens/ (npm run build in @cupped/tokens).
5
+
6
+ Import after Tailwind:
7
+ @import "tailwindcss";
8
+ @import "@cupped/tokens/tailwind";
9
+
10
+ Namespaced variables generate utilities (bg-canvas, text-ink-secondary,
11
+ rounded-lg, shadow-primary, font-serif, text-card-title, p-base…); the
12
+ trailing block re-exports the motion/material tokens as plain CSS
13
+ variables. Values here are fully resolved — alias var() chains live in
14
+ the "/css" export.
15
+ ════════════════════════════════════════════════════════════════ */
16
+
17
+ @theme {
18
+ /* ── Colors → bg-*, text-*, border-*, fill-*… ── */
19
+ --color-canvas: #F8FAFC;
20
+ --color-card: #FFFFFF;
21
+ --color-canvas-border: #E2E8F0;
22
+ --color-canvas-border-subtle: #F1F5F9;
23
+ --color-ink: #0F172A;
24
+ --color-ink-secondary: #475569;
25
+ --color-ink-muted: #94A3B8;
26
+ --color-ink-inverse: #F8FAFC;
27
+ --color-primary: #E07A5F;
28
+ --color-primary-strong: #C05539;
29
+ --color-primary-strong-hover: #AD4A30;
30
+ --color-primary-hover: #D16A4F;
31
+ --color-primary-light: #FDF2F0;
32
+ --color-primary-muted: rgba(224, 122, 95, 0.12);
33
+ --color-success: #22C55E;
34
+ --color-success-light: #F0FDF4;
35
+ --color-success-ink: #15803D;
36
+ --color-error: #EF4444;
37
+ --color-error-light: #FEF2F2;
38
+ --color-error-ink: #B91C1C;
39
+ --color-warning: #F59E0B;
40
+ --color-warning-light: #FFFBEB;
41
+ --color-warning-ink: #B45309;
42
+ --color-info: #3B82F6;
43
+ --color-info-light: #EFF6FF;
44
+ --color-info-ink: #1D4ED8;
45
+ --color-fruity: #B44C54;
46
+ --color-floral: #865CB2;
47
+ --color-nutty: #876F49;
48
+ --color-chocolate: #916951;
49
+ --color-spice: #B4503A;
50
+ --color-sweet: #8E6E09;
51
+ --color-citrus: #A65F02;
52
+ --color-green: #4E8429;
53
+ --color-berry: #AC4D7B;
54
+ --color-roasted: #896B5E;
55
+ --color-fruity-accessible: #9B494E;
56
+ --color-berry-accessible: #94496D;
57
+ --color-citrus-accessible: #925512;
58
+ --color-sweet-accessible: #7E6207;
59
+ --color-floral-accessible: #755498;
60
+ --color-green-accessible: #49742E;
61
+ --color-spice-accessible: #9B4C3A;
62
+ --color-nutty-accessible: #79623E;
63
+ --color-chocolate-accessible: #825C46;
64
+ --color-roasted-accessible: #7B5F52;
65
+ --color-on-flavor: #FFFFFF;
66
+ --color-xp: #8E6E09;
67
+ --color-xp-accessible: #7E6207;
68
+ --color-ember: #F97316;
69
+ --color-ember-accessible: #C2410C;
70
+ --color-streak: #F97316;
71
+ --color-streak-accessible: #C2410C;
72
+ --color-badge: #865CB2;
73
+ --color-badge-accessible: #755498;
74
+ --color-zone-ideal: #22C55E;
75
+ --color-zone-under: #3B82F6;
76
+ --color-zone-over: #EF4444;
77
+ --color-zone-strong: #F59E0B;
78
+ --color-zone-weak: #94A3B8;
79
+ --color-chart-sca: #E07A5F;
80
+ --color-chart-ucdavis: #865CB2;
81
+ --color-chart-grid: #E2E8F0;
82
+
83
+ /* ── Font families → font-sans, font-serif, font-mono ── */
84
+ --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
85
+ --font-serif: 'Instrument Serif', Georgia, serif;
86
+ --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
87
+
88
+ /* ── Type scale → text-screen-title, text-body… (size + line-height + weight) ── */
89
+ --text-screen-title: 32px;
90
+ --text-screen-title--line-height: 1.1;
91
+ --text-screen-title--font-weight: 700;
92
+ --text-sheet-title: 24px;
93
+ --text-sheet-title--line-height: 1.2;
94
+ --text-sheet-title--font-weight: 600;
95
+ --text-card-title: 20px;
96
+ --text-card-title--line-height: 1.25;
97
+ --text-card-title--font-weight: 600;
98
+ --text-body: 16px;
99
+ --text-body--line-height: 1.5;
100
+ --text-body--font-weight: 400;
101
+ --text-body-sm: 14px;
102
+ --text-body-sm--line-height: 1.45;
103
+ --text-body-sm--font-weight: 400;
104
+ --text-caption: 12px;
105
+ --text-caption--line-height: 1.4;
106
+ --text-caption--font-weight: 500;
107
+ --text-display: 56px;
108
+ --text-display--line-height: 1;
109
+ --text-display--font-weight: 400;
110
+ --text-display--letter-spacing: -0.015em;
111
+ --text-mono: 12px;
112
+ --text-mono--line-height: 1.4;
113
+ --text-mono--font-weight: 500;
114
+
115
+ /* ── Spacing → p-base, gap-sm, m-xxl… (4pt grid) ── */
116
+ --spacing-xs: 4px;
117
+ --spacing-sm: 8px;
118
+ --spacing-md: 12px;
119
+ --spacing-base: 16px;
120
+ --spacing-lg: 20px;
121
+ --spacing-xl: 24px;
122
+ --spacing-xxl: 32px;
123
+
124
+ /* ── Radii → rounded-sm…rounded-full ── */
125
+ --radius-sm: 8px;
126
+ --radius-md: 12px;
127
+ --radius-lg: 16px;
128
+ --radius-xl: 24px;
129
+ --radius-full: 9999px;
130
+
131
+ /* ── Elevation → shadow-sm, shadow-md, shadow-lg, shadow-primary ── */
132
+ --shadow-sm: 0 1px 2px rgba(15,23,42,0.04), 0 1px 1px rgba(15,23,42,0.03);
133
+ --shadow-md: 0 4px 12px rgba(15,23,42,0.06), 0 1px 3px rgba(15,23,42,0.04);
134
+ --shadow-lg: 0 12px 32px rgba(15,23,42,0.08), 0 4px 8px rgba(15,23,42,0.04);
135
+ --shadow-primary: 0 12px 28px rgba(224,122,95,0.28), 0 4px 10px rgba(224,122,95,0.18);
136
+
137
+ /* ── Motion, interaction & materials (plain variables) ── */
138
+ --spring-response: 0.55s;
139
+ --spring-damping: 0.75;
140
+ --tap-scale: 0.95;
141
+ --tap-opacity: 0.8;
142
+ --stagger-delay: 0.08s;
143
+ --shimmer-duration: 1.5s;
144
+ --motion-reduced: 120ms;
145
+ --focus-ring: 0 0 0 2px #FFFFFF, 0 0 0 4px #C05539;
146
+ --hit-target-min: 44px;
147
+ --material-chrome-bg: rgba(255, 255, 255, 0.95);
148
+ --material-chrome-blur: saturate(1.4) blur(20px);
149
+ --scrim: rgba(15, 23, 42, 0.45);
150
+ }
151
+
152
+ @media (prefers-reduced-motion: reduce) {
153
+ :root {
154
+ --spring-response: var(--motion-reduced);
155
+ --stagger-delay: 0s;
156
+ }
157
+ }
158
+
159
+ @media (prefers-reduced-transparency: reduce) {
160
+ :root {
161
+ --material-chrome-bg: var(--color-card);
162
+ --material-chrome-blur: none;
163
+ }
164
+ }
package/docs/expo.md ADDED
@@ -0,0 +1,141 @@
1
+ # Consuming @cupped/tokens from Expo / React Native
2
+
3
+ Requires Expo SDK 53+ / RN 0.79+ (Metro resolves package.json `"exports"` by
4
+ default). The top-level `main`/`types` fallback covers SDK 52. The
5
+ `shadow.*.boxShadow` shape targets RN 0.76+ on the New Architecture; the
6
+ `ios`/`android` shapes are the legacy props.
7
+
8
+ ## Install
9
+
10
+ **Primary — GitHub Packages.** Map the `@cupped` scope to GitHub Packages in
11
+ the app's `.npmrc`:
12
+
13
+ ```ini
14
+ # .npmrc
15
+ @cupped:registry=https://npm.pkg.github.com
16
+ # TODO: this registry needs a read token. Add the auth line for your setup, e.g.
17
+ # //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
18
+ ```
19
+
20
+ ```bash
21
+ npm install @cupped/tokens
22
+ ```
23
+
24
+ Pin the version in `package.json` as usual (`"@cupped/tokens": "^0.1.0"`).
25
+
26
+ **Fallback — git dependency (no registry auth).** Because `dist/` is committed,
27
+ the package also installs straight from git with no `.npmrc` / token setup:
28
+
29
+ ```bash
30
+ npm install github:ybird-labs/cupped-design-system#v0.1.0
31
+ ```
32
+
33
+ ## The theme.ts pattern
34
+
35
+ Define one app-side theme module and make it **the only file allowed to
36
+ import the package** — call sites go through `theme`, which leaves room for
37
+ a future dark-mode tier without touching components:
38
+
39
+ ```ts
40
+ // src/theme.ts
41
+ import { tokens, type FlavorName } from "@cupped/tokens";
42
+
43
+ export const theme = {
44
+ colors: tokens.color,
45
+ space: tokens.space,
46
+ radius: tokens.radius,
47
+ font: tokens.font,
48
+ type: tokens.type,
49
+ shadow: tokens.shadow,
50
+ motion: tokens.motion,
51
+ focusRing: tokens.focusRing,
52
+ material: tokens.material,
53
+ } as const;
54
+
55
+ export type Theme = typeof theme;
56
+ export type { FlavorName };
57
+ ```
58
+
59
+ Usage (see [examples/expo/Button.example.tsx](../examples/expo/Button.example.tsx)):
60
+
61
+ ```tsx
62
+ style={{
63
+ minHeight: theme.space.hitTargetMin, // 44
64
+ borderRadius: theme.radius.md, // 12
65
+ backgroundColor: theme.colors.primary.strong,
66
+ ...theme.shadow.primary.android, // or boxShadow / .ios per arch
67
+ }}
68
+
69
+ // Reanimated — exact conversion of the spec's response/damping spring:
70
+ withSpring(1, theme.motion.spring.reanimated) // {stiffness: 130.51, damping: 17.14, mass: 1}
71
+ // or: withSpring(1, theme.motion.spring.durationBased)
72
+
73
+ // Reduced motion stays app-side behavior:
74
+ const reduced = useReducedMotion();
75
+ const duration = reduced ? theme.motion.reduced : undefined; // 120ms crossfade
76
+ ```
77
+
78
+ Dimensions are unitless dp numbers; `type.*` styles have absolute
79
+ `lineHeight` and a single `fontFamily` string. `material.chromeBg` /
80
+ `material.blur` / `material.saturate` pair with `expo-blur`; the opaque
81
+ `colors.card` fallback is the default, and iOS should prefer native
82
+ materials.
83
+
84
+ ## Fonts
85
+
86
+ Use the `expo-font` config plugin with static per-weight files, named to
87
+ match PostScript names so iOS and Android share one `fontFamily`:
88
+
89
+ ```jsonc
90
+ // app.json
91
+ {
92
+ "expo": {
93
+ "plugins": [
94
+ ["expo-font", { "fonts": [
95
+ "./assets/fonts/Inter-Regular.ttf",
96
+ "./assets/fonts/Inter-Medium.ttf",
97
+ "./assets/fonts/Inter-SemiBold.ttf",
98
+ "./assets/fonts/Inter-Bold.ttf",
99
+ "./assets/fonts/InstrumentSerif-Regular.ttf",
100
+ "./assets/fonts/InstrumentSerif-Italic.ttf",
101
+ "./assets/fonts/JetBrainsMono-Regular.ttf",
102
+ "./assets/fonts/JetBrainsMono-Medium.ttf"
103
+ ] }]
104
+ ]
105
+ }
106
+ }
107
+ ```
108
+
109
+ ## Enforcing "no raw values"
110
+
111
+ ESLint config (ports the adherence patterns shipped with the original
112
+ design-system export):
113
+
114
+ ```jsonc
115
+ {
116
+ "rules": {
117
+ "react-native/no-color-literals": "error",
118
+ "no-restricted-imports": ["error", {
119
+ "paths": [{
120
+ "name": "@cupped/tokens",
121
+ "message": "Import the app theme from src/theme.ts instead."
122
+ }],
123
+ "patterns": ["@cupped/tokens/**"]
124
+ }],
125
+ "no-restricted-syntax": [
126
+ "error",
127
+ {
128
+ "selector": "Literal[value=/#[0-9a-fA-F]{3,8}\\b/]",
129
+ "message": "Raw hex color — use a design-system token via theme."
130
+ },
131
+ {
132
+ "selector": "Literal[value=/\\b\\d+px\\b/]",
133
+ "message": "Raw px value — use a design-system spacing token via theme."
134
+ }
135
+ ]
136
+ },
137
+ "overrides": [
138
+ { "files": ["src/theme.ts"], "rules": { "no-restricted-imports": "off" } }
139
+ ]
140
+ }
141
+ ```