@mindvalley/design-system 4.0.1 → 4.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,298 @@
1
+ # Design token validation with Zod
2
+
3
+ This document describes the **internal** token validation system implemented using Zod for runtime validation and TypeScript type inference. These utilities are used during the build process and are not exported to package consumers.
4
+
5
+ ## Overview
6
+
7
+ The Mindvalley Design System uses Supernova to manage design tokens, which are pushed to the repository as JSON. We use Zod for validation that:
8
+
9
+ - Validates token structure at build time
10
+ - Provides TypeScript types derived from the same schemas
11
+ - Offers clear error messages when tokens don't match expected structure
12
+ - Handles the complex, nested structures that Supernova generates
13
+
14
+ ## Architecture
15
+
16
+ ### Core files
17
+
18
+ - `src/types/token-schemas.ts` - Zod schemas defining token structure
19
+ - `src/types/token-validation.ts` - Validation utilities and functions
20
+ - `src/types/index.d.ts` - TypeScript type exports
21
+
22
+ ### Token structure
23
+
24
+ Supernova generates tokens with varying levels of nesting:
25
+
26
+ ```json
27
+ {
28
+ "color": {
29
+ "primary": {
30
+ "500": {
31
+ "value": "#ff0000ff",
32
+ "type": "color"
33
+ }
34
+ },
35
+ "black": {
36
+ "value": "#000000ff",
37
+ "type": "color"
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ ## For package consumers
44
+
45
+ If you're using the `@mindvalley/design-system` npm package, the validation happens at build time and you receive:
46
+
47
+ - Type-safe `ColorTokens` interface for the colors object
48
+ - `TypographyToken` and `TypographySet` types from the Tailwind plugin
49
+ - Pre-validated, transformed tokens ready for use
50
+
51
+ The examples below are for **contributors and maintainers** working on the design system itself.
52
+
53
+ ## Usage guide (internal development)
54
+
55
+ ### 1. Basic validation
56
+
57
+ ```typescript
58
+ import { validateTokenFile } from './types/token-validation';
59
+
60
+ const result = validateTokenFile('src/tokens/brands/mindvalley/colors.json');
61
+ if (result.success) {
62
+ console.log('Tokens are valid!', result.data);
63
+ } else {
64
+ console.error('Validation failed:', result.errors);
65
+ }
66
+ ```
67
+
68
+ ### 2. Type-safe token access
69
+
70
+ ```typescript
71
+ import { ColorToken } from './types/token-schemas';
72
+ import { validateColorToken } from './types/token-validation';
73
+
74
+ // Runtime validation with automatic type inference
75
+ try {
76
+ const token: ColorToken = validateColorToken(unknownData);
77
+ // TypeScript knows token.value is a hex string
78
+ console.log(token.value);
79
+ } catch (error) {
80
+ console.error('Invalid color token:', error);
81
+ }
82
+ ```
83
+
84
+ ### 3. Batch validation
85
+
86
+ ```typescript
87
+ import { validateAllTokens, getValidationSummary } from './types/token-validation';
88
+
89
+ // Validate all tokens in the project
90
+ const summary = getValidationSummary('./src/tokens');
91
+ console.log(`Valid: ${summary.valid}, Invalid: ${summary.invalid}`);
92
+
93
+ if (summary.invalid > 0) {
94
+ summary.errors.forEach(error => {
95
+ console.error(`❌ ${error.file}:`, error.errors);
96
+ });
97
+ }
98
+ ```
99
+
100
+ ### 4. CI/CD integration
101
+
102
+ Add to your `package.json`:
103
+
104
+ ```json
105
+ {
106
+ "scripts": {
107
+ "validate-tokens": "ts-node src/scripts/validate-tokens.ts",
108
+ "prebuild": "npm run validate-tokens"
109
+ }
110
+ }
111
+ ```
112
+
113
+ Create `src/scripts/validate-tokens.ts`:
114
+
115
+ ```typescript
116
+ import { getValidationSummary } from '../types/token-validation';
117
+
118
+ const summary = getValidationSummary('./src/tokens');
119
+
120
+ if (summary.invalid > 0) {
121
+ console.error('Token validation failed!');
122
+ summary.errors.forEach(error => {
123
+ console.error(`❌ ${error.file}:`, error.errors.join('\n '));
124
+ });
125
+ process.exit(1);
126
+ }
127
+
128
+ console.log('✅ All tokens validated successfully!');
129
+ ```
130
+
131
+ ### 5. Type guards
132
+
133
+ ```typescript
134
+ import { isColorToken, isTypographyToken } from './types/token-validation';
135
+
136
+ function processToken(token: unknown) {
137
+ if (isColorToken(token)) {
138
+ // TypeScript knows this is a ColorToken
139
+ return createColorVariable(token.value);
140
+ } else if (isTypographyToken(token)) {
141
+ // TypeScript knows this is a TypographyToken
142
+ return createTypographyStyles(token.value);
143
+ }
144
+ }
145
+ ```
146
+
147
+ ### 6. Error handling
148
+
149
+ ```typescript
150
+ import { safeValidate, ColorFileSchema, formatZodError } from './types/token-validation';
151
+
152
+ function handleSupernovaWebhook(payload: unknown) {
153
+ const result = safeValidate(ColorFileSchema, payload);
154
+
155
+ if (!result.success) {
156
+ const errors = formatZodError(result.errors);
157
+
158
+ // Log structured errors
159
+ console.error('Token validation failed:', {
160
+ errors,
161
+ timestamp: new Date().toISOString(),
162
+ source: 'supernova-webhook'
163
+ });
164
+
165
+ // Alert team
166
+ notifySlack({
167
+ channel: '#design-system',
168
+ message: `Token validation failed: ${errors.join(', ')}`
169
+ });
170
+
171
+ return;
172
+ }
173
+
174
+ // Process valid tokens
175
+ updateTokens(result.data);
176
+ }
177
+ ```
178
+
179
+ ## Testing
180
+
181
+ ### Unit tests
182
+
183
+ ```typescript
184
+ import { ColorTokenSchema } from './types/token-schemas';
185
+
186
+ describe('Color Token Validation', () => {
187
+ it('should accept valid color tokens', () => {
188
+ const validToken = {
189
+ value: '#ff0000ff',
190
+ type: 'color'
191
+ };
192
+
193
+ expect(() => ColorTokenSchema.parse(validToken)).not.toThrow();
194
+ });
195
+
196
+ it('should reject invalid hex values', () => {
197
+ const invalidToken = {
198
+ value: 'not-a-color',
199
+ type: 'color'
200
+ };
201
+
202
+ expect(() => ColorTokenSchema.parse(invalidToken)).toThrow();
203
+ });
204
+ });
205
+ ```
206
+
207
+ ### Integration tests
208
+
209
+ ```typescript
210
+ import { validateAllTokens } from './types/token-validation';
211
+
212
+ describe('Token File Validation', () => {
213
+ it('should validate all production tokens', () => {
214
+ const results = validateAllTokens('./src/tokens');
215
+ const failures = results.filter(r => !r.success);
216
+
217
+ expect(failures).toHaveLength(0);
218
+ });
219
+ });
220
+ ```
221
+
222
+ ## Schema flexibility
223
+
224
+ The schemas are designed to handle Supernova's complex output:
225
+
226
+ - **Nested structures**: Tokens can be nested at varying levels
227
+ - **Optional properties**: Some tokens have optional fields like `comment`
228
+ - **Mixed structures**: Different brands may have slightly different structures
229
+ - **Type variations**: Properties like `textCase` can be strings or objects
230
+
231
+ ## Extending the system
232
+
233
+ ### Adding new token types
234
+
235
+ 1. Add the schema in `token-schemas.ts`:
236
+
237
+ ```typescript
238
+ export const NewTokenSchema = z.object({
239
+ value: z.string(),
240
+ type: z.literal('newtype'),
241
+ metadata: z.object({
242
+ // Define metadata structure
243
+ }).optional()
244
+ });
245
+ ```
246
+
247
+ 2. Add to file schema mapping in `token-validation.ts`:
248
+
249
+ ```typescript
250
+ const FILE_SCHEMA_MAP = {
251
+ // existing mappings...
252
+ 'newtokens.json': NewTokenFileSchema
253
+ };
254
+ ```
255
+
256
+ ### Custom validation rules
257
+
258
+ ```typescript
259
+ const CustomColorSchema = ColorTokenSchema.refine(
260
+ (token) => token.value.length === 9, // RGBA with alpha
261
+ { message: 'Colors must include alpha channel' }
262
+ );
263
+ ```
264
+
265
+ ## Troubleshooting
266
+
267
+ ### Common issues
268
+
269
+ 1. **"Invalid input" errors**: Check if Supernova changed the token structure
270
+ 2. **Missing properties**: Some tokens may have optional fields
271
+ 3. **Type mismatches**: Use the flexible schemas that accept `z.any()` for complex nested structures
272
+
273
+ ### Debugging
274
+
275
+ ```typescript
276
+ import { validateTokenFile, getErrorReport } from './types/token-validation';
277
+
278
+ const result = validateTokenFile('path/to/tokens.json');
279
+ if (!result.success) {
280
+ // Get detailed error report
281
+ console.log(getErrorReport(result.errors));
282
+ }
283
+ ```
284
+
285
+ ## Best practices
286
+
287
+ 1. **Run validation in CI/CD**: Catch issues before they reach production
288
+ 2. **Validate after Supernova updates**: Add webhook handlers that validate incoming tokens
289
+ 3. **Use type guards**: Leverage TypeScript's type narrowing with `isColorToken()` etc.
290
+ 4. **Keep schemas updated**: When Supernova changes output format, update schemas accordingly
291
+ 5. **Test with real data**: Always test schemas against actual token files
292
+
293
+ ## Future improvements
294
+
295
+ - Add schema versioning to handle Supernova format changes
296
+ - Create a CLI tool for token validation
297
+ - Add visual regression tests for token changes
298
+ - Implement token transformation pipelines with validation
@@ -0,0 +1,58 @@
1
+ # Typography
2
+
3
+ Typography utilities ship as Tailwind CSS plugins. This documentation covers setup, configuration, and usage for both the current and legacy typography systems.
4
+
5
+ ## Quick links
6
+
7
+ | Guide | Description |
8
+ |-------|-------------|
9
+ | [Setup Guide](setup.md) | Font loading and Tailwind plugin configuration |
10
+ | [Migration Guide](migration.md) | Migrating from Sharp Grotesk to Google Sans Flex |
11
+
12
+ ## Typography systems
13
+
14
+ ### Google Sans Flex (recommended)
15
+
16
+ The current typography system uses Google Sans Flex, a variable font that supports:
17
+
18
+ - Multiple weights (400-700) in a single file
19
+ - Width axis (`wdth`) for condensed/normal text
20
+ - Slant axis (`slnt`) for italic variants
21
+ - 3-tier responsive breakpoints (mobile, tablet, desktop)
22
+
23
+ ### Sharp Grotesk (legacy)
24
+
25
+ The legacy typography system uses Sharp Grotesk static fonts. It remains available for backward compatibility but is frozen and will not receive updates.
26
+
27
+ ## Class reference
28
+
29
+ ### Body text
30
+
31
+ | Class | Description |
32
+ |-------|-------------|
33
+ | `body` | Default body text |
34
+ | `body-bold` | Bold body text |
35
+ | `body-italic` | Italic body text (GSF only) |
36
+ | `body-large`, `body-small`, `body-xs` | Size variants |
37
+
38
+ ### Titles (responsive)
39
+
40
+ | Class | Description |
41
+ |-------|-------------|
42
+ | `title-1` through `title-6` | Responsive titles (scale across breakpoints) |
43
+ | `title-7` through `title-11` | Non-responsive titles |
44
+ | `title-bold-1` through `title-bold-5` | Bold responsive titles |
45
+
46
+ ### Component text
47
+
48
+ | Class | Description |
49
+ |-------|-------------|
50
+ | `button-text`, `button-text-medium`, `button-text-small` | Button labels |
51
+ | `caption-disclaimer` | Captions and disclaimers |
52
+ | `overline-text` | Overline text |
53
+ | `timer-text`, `timer-text-medium`, `timer-text-small` | Timer displays |
54
+
55
+ ## Related
56
+
57
+ - [Usage Guide](../usage.md#-typography) - Typography section in main usage guide
58
+ - [Colors](../usage.md#-colors) - Color system documentation
@@ -0,0 +1,166 @@
1
+ # Migrating to Google Sans Flex
2
+
3
+ This guide helps you migrate from Sharp Grotesk typography to Google Sans Flex (GSF).
4
+
5
+ ## Overview
6
+
7
+ Google Sans Flex is the new typography system. Sharp Grotesk remains available for backward compatibility but is frozen and will not receive updates.
8
+
9
+ ## Migration steps
10
+
11
+ ### 1. Update font loading
12
+
13
+ **Before (Sharp Grotesk):**
14
+
15
+ ```typescript
16
+ import '@mindvalley/design-system/typography/mindvalley/fonts.css'
17
+ ```
18
+
19
+ **After (Google Sans Flex):**
20
+
21
+ Option A - Add to your HTML `<head>`:
22
+
23
+ ```html
24
+ <link rel="preconnect" href="https://fonts.googleapis.com">
25
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
26
+ <link href="https://fonts.googleapis.com/css2?family=Google+Sans+Flex:wdth,wght@75..125,100..900&display=swap" rel="stylesheet">
27
+ ```
28
+
29
+ Option B - CSS @import (Tailwind v4):
30
+
31
+ ```css
32
+ /* Font import must come FIRST */
33
+ @import "https://fonts.googleapis.com/css2?family=Google+Sans+Flex:wdth,wght@75..125,100..900&display=swap";
34
+
35
+ @import "tailwindcss";
36
+
37
+ @config "./tailwind.config.js";
38
+ ```
39
+
40
+ > See [Setup Guide](setup.md) for detailed framework examples.
41
+
42
+ ### 2. Update Tailwind plugin
43
+
44
+ **Before:**
45
+
46
+ ```typescript
47
+ import typography from '@mindvalley/design-system/tailwind/plugins/typography'
48
+ ```
49
+
50
+ **After:**
51
+
52
+ ```typescript
53
+ import typography from '@mindvalley/design-system/tailwind/plugins/typography-gsf'
54
+ ```
55
+
56
+ ### 3. Test your app
57
+
58
+ Class names remain the same - your existing markup should work without modification:
59
+
60
+ ```html
61
+ <!-- These classes work with both plugins -->
62
+ <h1 class="title-bold-1">Heading</h1>
63
+ <p class="body">Body text</p>
64
+ <button class="button-text">Click me</button>
65
+ ```
66
+
67
+ ---
68
+
69
+ ## What changed
70
+
71
+ ### Font family
72
+
73
+ | System | Font Family |
74
+ |--------|-------------|
75
+ | Sharp Grotesk | `'Sharp Grotesk Cyr Book 19', Helvetica, Arial, sans-serif` |
76
+ | Google Sans Flex | `'Google Sans Flex', Helvetica, Arial, sans-serif` |
77
+
78
+ ### Responsive breakpoints
79
+
80
+ | System | Breakpoints |
81
+ |--------|-------------|
82
+ | Sharp Grotesk | 2-tier: mobile, desktop |
83
+ | Google Sans Flex | 3-tier: mobile, tablet (768px), desktop (1024px) |
84
+
85
+ ### Font weights
86
+
87
+ GSF uses CSS `font-weight` values instead of multiple font family files:
88
+
89
+ | Weight | Sharp Grotesk | Google Sans Flex |
90
+ |--------|---------------|------------------|
91
+ | Regular | `Sharp Grotesk Cyr Book 19` | `font-weight: 400` |
92
+ | Medium | `Sharp Grotesk Cyr Medium 20` | `font-weight: 500` |
93
+ | Semibold | `Sharp Grotesk Cyr Semibold 20` | `font-weight: 600` |
94
+ | Bold | - | `font-weight: 700` |
95
+
96
+ ### Variable font axes
97
+
98
+ GSF is a variable font with two axes:
99
+
100
+ | Axis | CSS Property | Description |
101
+ |------|--------------|-------------|
102
+ | `wdth` | `font-variation-settings` | Width (92 for body, 100 for titles) |
103
+ | `slnt` | `font-variation-settings` | Slant (-10 for italics) |
104
+
105
+ ### New italic variants (GSF only)
106
+
107
+ GSF introduces italic variants:
108
+
109
+ - `body-italic`, `body-bold-italic`
110
+ - `body-large-italic`, `body-large-bold-italic`
111
+ - `body-small-italic`, `body-small-bold-italic`
112
+ - `body-xs-italic`
113
+
114
+ ---
115
+
116
+ ## Running both systems
117
+
118
+ > **Note:** The recommended approach is to migrate fully from Sharp Grotesk to Google Sans Flex. Running both systems simultaneously is not recommended for production use, but is shown here as a technique for gradual migration or A/B testing.
119
+
120
+ If needed, you can run both typography systems side-by-side using prefixes:
121
+
122
+ ```javascript
123
+ // tailwind.config.js
124
+ const sharpGrotesk = require('@mindvalley/design-system/tailwind/plugins/typography').default
125
+ const googleSansFlex = require('@mindvalley/design-system/tailwind/plugins/typography-gsf').default
126
+
127
+ module.exports = {
128
+ plugins: [
129
+ sharpGrotesk(),
130
+ googleSansFlex({ prefix: 'gsf-' }), // or whatever prefix you need.
131
+ ],
132
+ }
133
+ ```
134
+
135
+ > **Warning:** Both plugins generate identical class names (`.body`, `.title-1`, etc.). Using prefixes is **required** when running both systems together to avoid conflicts. Without prefixes, the second plugin will override the first.
136
+
137
+ ```html
138
+ <!-- Sharp Grotesk -->
139
+ <h1 class="title-bold-1">Heading</h1>
140
+
141
+ <!-- Google Sans Flex -->
142
+ <h1 class="gsf-title-bold-1">Heading</h1>
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Troubleshooting
148
+
149
+ ### Font not loading
150
+
151
+ Ensure you've added the Google Fonts link to your HTML `<head>`.
152
+
153
+ ### Missing tablet breakpoint styles
154
+
155
+ The GSF plugin supports 3-tier responsive. Ensure you're using the latest version of the design system.
156
+
157
+ ### Class names not generated
158
+
159
+ Check that your Tailwind content paths include files using typography classes so the JIT compiler includes them.
160
+
161
+ ---
162
+
163
+ ## Related
164
+
165
+ - [Setup Guide](setup.md) - Complete setup instructions
166
+ - [Typography Overview](README.md) - Class reference