@nons-dev/uikit 0.1.5 → 0.1.6

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.
@@ -2660,6 +2660,6 @@
2660
2660
  "schemaExample": "{\n \"type\": \"side-panel\",\n \"props\": {}\n}"
2661
2661
  }
2662
2662
  ],
2663
- "scannedAt": "2026-06-26T16:53:20.600Z",
2663
+ "scannedAt": "2026-06-27T05:59:11.393Z",
2664
2664
  "totalFiles": 53
2665
2665
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nons-dev/uikit",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "license": "BUSL-1.1",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/readme.md CHANGED
@@ -49,23 +49,44 @@ The `nons.config.yaml` file at your project root is the **single source of truth
49
49
 
50
50
  ```yaml
51
51
  theme:
52
- preset: nons # theme preset (nons, custom)
52
+ preset: nons
53
53
 
54
54
  brand:
55
- name: Nons # brand name
55
+ name: Nons
56
56
 
57
57
  direction:
58
- default: rtl # UI direction (rtl, ltr)
58
+ default: rtl
59
59
 
60
60
  locale:
61
- default: fa # default language
62
- supported: [fa, en] # supported languages
61
+ default: fa
62
+ supported: [fa, en]
63
63
 
64
64
  font:
65
65
  fa:
66
66
  family: IRANSansX
67
67
  en:
68
68
  family: Satoshi
69
+
70
+ tokens:
71
+ colors:
72
+ light:
73
+ --color-primary: '#9FE870'
74
+ --color-danger: '#F31260'
75
+ # ... override any CSS custom property
76
+ dark:
77
+ --color-primary: '#9FE870'
78
+ --color-danger: '#f31260'
79
+ spacing:
80
+ --space-4: 16px
81
+ # ... typography, radius, shadows, z-index, opacity, button, alert
82
+
83
+ theme-mappings:
84
+ light:
85
+ --bg-primary: var(--color-ui-window)
86
+ --text-primary: var(--color-text-default)
87
+ dark:
88
+ --bg-primary: var(--color-ui-window)
89
+ --text-primary: var(--color-text-default)
69
90
  ```
70
91
 
71
92
  Run `npx @nons-dev/uikit generate` to regenerate CSS variables from your config.
@@ -81,6 +102,26 @@ npx @nons-dev/uikit schema <name> # Show schema JSON for a component
81
102
  npx @nons-dev/uikit search <query> # Search components by name or description
82
103
  ```
83
104
 
105
+ ## Customizing Colors & Tokens
106
+
107
+ Edit `nons.config.yaml` in your project root, then regenerate:
108
+
109
+ ```bash
110
+ npx @nons-dev/uikit generate
111
+ ```
112
+
113
+ This produces `generated/tokens.css` (color schemes + all tokens) and `generated/theme.css` (semantic theme mappings). Import them in your app after the uikit CSS:
114
+
115
+ ```ts
116
+ import '@nons-dev/uikit/style.css'
117
+ import './generated/tokens.css'
118
+ import './generated/theme.css'
119
+ ```
120
+
121
+ The generated CSS uses `:root` / `[data-theme="dark"]` blocks — your custom values override the defaults with the same specificity.
122
+
123
+ All visual values in the uikit reference CSS custom properties (`var(--color-primary)`), so changing a token in YAML propagates to every component.
124
+
84
125
  ## Documentation
85
126
 
86
127
  | Resource | Description |
package/scripts/cli.mjs CHANGED
@@ -16,7 +16,6 @@ const GREEN = '\x1b[32m'
16
16
  const CYAN = '\x1b[36m'
17
17
  const YELLOW = '\x1b[33m'
18
18
  const MAGENTA = '\x1b[35m'
19
- const WHITE = '\x1b[37m'
20
19
  const GRAY = '\x1b[90m'
21
20
  const RED = '\x1b[31m'
22
21
  const BLUE = '\x1b[34m'
@@ -41,6 +40,168 @@ font:
41
40
  family: IRANSansX
42
41
  en:
43
42
  family: Satoshi
43
+ # Faces loaded automatically from @nons-dev/uikit/dist/font/
44
+ # Uncomment and customize src to use your own font files:
45
+ # faces:
46
+ # - family: Satoshi
47
+ # weight: 400
48
+ # src: fonts/Satoshi-Regular.woff2
49
+ # format: woff2
50
+ # - family: IRANSansX
51
+ # weight: 400
52
+ # src: fonts/IRANSansX-Regular.woff
53
+ # format: woff
54
+
55
+ tokens:
56
+ colors:
57
+ light:
58
+ --color-text-default: '#37352F'
59
+ --color-text-grey: '#787774'
60
+ --color-ui-window: '#FFFFFF'
61
+ --color-ui-sidebar: '#F7F6F3'
62
+ --color-ui-hover: rgba(55, 53, 47, 0.08)
63
+ --color-primary: '#9FE870'
64
+ --color-primary-bg: rgba(159, 232, 112, 0.15)
65
+ --color-primary-dark: '#163300'
66
+ --color-primary-text: var(--color-primary-dark)
67
+ --color-primary-selected-bg: rgba(159, 232, 112, 0.08)
68
+ --color-danger: '#F31260'
69
+ --color-danger-bg: '#FEE7EF'
70
+ --color-success: '#17C964'
71
+ --color-success-bg: '#E8FAF0'
72
+ --color-warning: '#F5A524'
73
+ --color-warning-bg: '#FEFCE8'
74
+ --color-secondary: '#7828C8'
75
+ --color-secondary-bg: '#F2EAFA'
76
+ --color-info: '#006FEE'
77
+ --color-info-bg: '#E7F3F8'
78
+ --color-default: '#11181C'
79
+ --color-default-bg: '#F4F4F5'
80
+ --color-border: '#D5D5D2'
81
+ --color-text-secondary: var(--color-text-grey)
82
+ --color-white: '#FFFFFF'
83
+ dark:
84
+ --color-text-default: '#FFFFFF'
85
+ --color-text-grey: '#979A9B'
86
+ --color-ui-window: '#111111'
87
+ --color-ui-sidebar: '#000000'
88
+ --color-ui-hover: rgba(255, 255, 255, 0.08)
89
+ --color-primary: '#9FE870'
90
+ --color-primary-bg: rgba(159, 232, 112, 0.15)
91
+ --color-primary-dark: '#163300'
92
+ --color-primary-text: var(--color-primary-dark)
93
+ --color-primary-selected-bg: rgba(159, 232, 112, 0.08)
94
+ --color-danger: '#f31260'
95
+ --color-danger-bg: '#310413'
96
+ --color-success: '#17c964'
97
+ --color-success-bg: '#052814'
98
+ --color-warning: '#f5a524'
99
+ --color-warning-bg: '#312107'
100
+ --color-secondary: '#9353d3'
101
+ --color-secondary-bg: '#180828'
102
+ --color-info: '#006fee'
103
+ --color-info-bg: '#001731'
104
+ --color-default: '#006fee'
105
+ --color-default-bg: '#001731'
106
+ --color-border: '#1D1D1D'
107
+ --color-text-secondary: var(--color-text-grey)
108
+ --color-white: '#FFFFFF'
109
+
110
+ spacing:
111
+ --space-0-5: 2px
112
+ --space-1: 4px
113
+ --space-2: 8px
114
+ --space-2-5: 10px
115
+ --space-3: 12px
116
+ --space-4: 16px
117
+ --space-5: 24px
118
+ --space-6: 32px
119
+ --space-8: 48px
120
+ --space-10: 64px
121
+ --space-3-5: 14px
122
+ --space-4-5: 18px
123
+ --border-width: 1px
124
+ --border-width-lg: 2px
125
+
126
+ typography:
127
+ --font-family: "'IRANSansX', 'Satoshi', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif"
128
+ --font-family-mono: "'SF Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', monospace"
129
+ --font-size-xs: 12px
130
+ --font-size-sm: 12px
131
+ --font-size-md: 14px
132
+ --font-size-lg: 16px
133
+ --font-size-xl: 18px
134
+ --font-size-2xl: 24px
135
+ --font-weight-normal: 400
136
+ --font-weight-medium: 500
137
+ --font-weight-bold: 700
138
+ --line-height-sm: 18px
139
+ --line-height-md: 22px
140
+ --line-height-lg: 30px
141
+
142
+ radius:
143
+ --radius-sm: 4px
144
+ --radius-md: 8px
145
+ --radius-lg: 12px
146
+ --radius-xl: 16px
147
+ --radius-full: 9999px
148
+
149
+ shadows:
150
+ light:
151
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06)
152
+ --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.08)
153
+ --shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.1)
154
+ dark:
155
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3)
156
+ --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.35)
157
+ --shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.4)
158
+
159
+ z-index:
160
+ --z-dropdown: 100
161
+ --z-popover: 90
162
+ --z-tooltip: 150
163
+ --z-modal: 1000
164
+ --z-panel: 200
165
+
166
+ opacity:
167
+ --opacity-disabled: 0.5
168
+ --opacity-secondary: 0.6
169
+ --opacity-placeholder: 0.6
170
+ --opacity-hint: 0.8
171
+ --opacity-hover: 0.8
172
+
173
+ button:
174
+ --btn-border-radius: 8px
175
+ --btn-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05)
176
+ --btn-padding: 10px 16px
177
+ --btn-font-size: 14px
178
+ --btn-font-weight: 500
179
+ --btn-font-family: "'IRANSansX', sans-serif"
180
+
181
+ alert:
182
+ --alert-border-radius: 16px
183
+ --alert-padding: 12px
184
+ --alert-gap: 16px
185
+
186
+ theme-mappings:
187
+ light:
188
+ --bg-primary: var(--color-ui-window)
189
+ --bg-sidebar: var(--color-ui-sidebar)
190
+ --bg-hover: var(--color-ui-hover)
191
+ --bg-card: var(--color-ui-window)
192
+ --text-primary: var(--color-text-default)
193
+ --text-secondary: var(--color-text-secondary)
194
+ --border-primary: var(--color-border)
195
+ --overlay-bg: rgba(0, 0, 0, 0.4)
196
+ dark:
197
+ --bg-primary: var(--color-ui-window)
198
+ --bg-sidebar: var(--color-ui-sidebar)
199
+ --bg-hover: var(--color-ui-hover)
200
+ --bg-card: '#161816'
201
+ --text-primary: var(--color-text-default)
202
+ --text-secondary: var(--color-text-grey)
203
+ --border-primary: var(--color-border)
204
+ --overlay-bg: rgba(0, 0, 0, 0.6)
44
205
  `
45
206
 
46
207
  // ── Data ──
@@ -70,6 +231,111 @@ function searchComponents(data, query) {
70
231
  )
71
232
  }
72
233
 
234
+ // ── Generate helpers ──
235
+
236
+ function writeTokenBlock(tokens, indent) {
237
+ return Object.entries(tokens || {}).map(([k, v]) => `${indent}${k}: ${v};`).join('\n')
238
+ }
239
+
240
+ function generateFontCSS(config) {
241
+ const faces = config.font?.faces
242
+ if (!faces || !faces.length) return ''
243
+ const lines = []
244
+ for (const face of faces) {
245
+ lines.push('@font-face {')
246
+ lines.push(` font-family: '${face.family}';`)
247
+ lines.push(` src: url('./${face.src}') format('${face.format}');`)
248
+ lines.push(` font-weight: ${face.weight};`)
249
+ lines.push(' font-style: normal;')
250
+ lines.push(' font-display: swap;')
251
+ lines.push('}')
252
+ lines.push('')
253
+ }
254
+ return lines.join('\n')
255
+ }
256
+
257
+ function generateTokensCSS(config) {
258
+ const lines = []
259
+ const fonts = generateFontCSS(config)
260
+ if (fonts) lines.push(fonts)
261
+
262
+ const t = config.tokens || {}
263
+
264
+ // Light & dark color schemes
265
+ const hasLight = t.colors?.light && Object.keys(t.colors.light).length
266
+ const hasDark = t.colors?.dark && Object.keys(t.colors.dark).length
267
+
268
+ if (hasLight) {
269
+ lines.push(':root {')
270
+ lines.push(' color-scheme: light;')
271
+ lines.push(writeTokenBlock(t.colors.light, ' '))
272
+ lines.push('}')
273
+ lines.push('')
274
+ }
275
+
276
+ if (hasDark) {
277
+ lines.push('[data-theme="dark"] {')
278
+ lines.push(' color-scheme: dark;')
279
+ lines.push(writeTokenBlock(t.colors.dark, ' '))
280
+ lines.push('}')
281
+ lines.push('')
282
+ }
283
+
284
+ // Flat token sections (no light/dark split)
285
+ const flatSections = {
286
+ spacing: 'Spacing', typography: 'Typography', radius: 'Border Radius',
287
+ 'z-index': 'Z-Index', opacity: 'Opacity', button: 'Button', alert: 'Alert',
288
+ }
289
+ for (const [section, label] of Object.entries(flatSections)) {
290
+ const tokens = t[section]
291
+ if (!tokens || !Object.keys(tokens).length) continue
292
+ lines.push(`/* ${label} */`)
293
+ lines.push(':root {')
294
+ lines.push(writeTokenBlock(tokens, ' '))
295
+ lines.push('}')
296
+ lines.push('')
297
+ }
298
+
299
+ // Shadows (light + dark)
300
+ const hasShadowLight = t.shadows?.light && Object.keys(t.shadows.light).length
301
+ const hasShadowDark = t.shadows?.dark && Object.keys(t.shadows.dark).length
302
+ if (hasShadowLight) {
303
+ lines.push(':root {')
304
+ lines.push(' color-scheme: light;')
305
+ lines.push(writeTokenBlock(t.shadows.light, ' '))
306
+ lines.push('}')
307
+ lines.push('')
308
+ }
309
+ if (hasShadowDark) {
310
+ lines.push('[data-theme="dark"] {')
311
+ lines.push(' color-scheme: dark;')
312
+ lines.push(writeTokenBlock(t.shadows.dark, ' '))
313
+ lines.push('}')
314
+ lines.push('')
315
+ }
316
+
317
+ return lines.join('\n')
318
+ }
319
+
320
+ function generateThemeCSS(config) {
321
+ const m = config['theme-mappings']
322
+ if (!m) return ''
323
+ const lines = []
324
+
325
+ lines.push(':root {')
326
+ lines.push(' color-scheme: light;')
327
+ if (m.light) lines.push(writeTokenBlock(m.light, ' '))
328
+ lines.push('}')
329
+
330
+ lines.push('')
331
+ lines.push('[data-theme="dark"] {')
332
+ lines.push(' color-scheme: dark;')
333
+ if (m.dark) lines.push(writeTokenBlock(m.dark, ' '))
334
+ lines.push('}')
335
+
336
+ return lines.join('\n')
337
+ }
338
+
73
339
  // ── Commands ──
74
340
 
75
341
  function cmdInit() {
@@ -93,16 +359,19 @@ function cmdGenerate() {
93
359
  const generatedDir = path.join(CWD, 'generated')
94
360
  if (!fs.existsSync(generatedDir)) fs.mkdirSync(generatedDir, { recursive: true })
95
361
 
96
- const lines = [':root {']
97
- const sections = ['colors', 'spacing', 'typography', 'radius', 'z-index', 'opacity', 'button', 'alert']
98
- for (const section of sections) {
99
- const tokens = config.tokens?.[section]
100
- if (!tokens) continue
101
- for (const [key, val] of Object.entries(tokens)) lines.push(` ${key}: ${val};`)
102
- }
103
- lines.push('}')
104
- fs.writeFileSync(path.join(generatedDir, 'tokens.css'), lines.join('\n'), 'utf-8')
362
+ // tokens.css
363
+ const tokensCSS = generateTokensCSS(config)
364
+ fs.writeFileSync(path.join(generatedDir, 'tokens.css'), tokensCSS, 'utf-8')
105
365
  console.log(`${GREEN}✓${RESET} generated/tokens.css`)
366
+
367
+ // theme.css
368
+ const themeCSS = generateThemeCSS(config)
369
+ if (themeCSS) {
370
+ fs.writeFileSync(path.join(generatedDir, 'theme.css'), themeCSS, 'utf-8')
371
+ console.log(`${GREEN}✓${RESET} generated/theme.css`)
372
+ }
373
+
374
+ console.log(`${YELLOW}ℹ${RESET} Import in your app:\n import './generated/tokens.css'\n import './generated/theme.css'`)
106
375
  }
107
376
 
108
377
  function cmdList() {
@@ -225,19 +494,19 @@ function printHelp() {
225
494
  console.log(`\n${BOLD}${CYAN}Nons UIKit CLI${RESET}\n`)
226
495
  console.log(`${BOLD}Commands:${RESET}\n`)
227
496
  console.log(` ${GREEN}init${RESET} Create nons.config.yaml in current directory`)
228
- console.log(` ${GREEN}generate${RESET} Generate CSS from nons.config.yaml`)
497
+ console.log(` ${GREEN}generate${RESET} Generate tokens.css + theme.css from config`)
229
498
  console.log(` ${GREEN}list${RESET} List all components`)
230
499
  console.log(` ${GREEN}show${RESET} ${DIM}<name>${RESET} Show full detail for a component`)
231
500
  console.log(` ${GREEN}schema${RESET} ${DIM}<name>${RESET} Show schema JSON for a component`)
232
501
  console.log(` ${GREEN}search${RESET} ${DIM}<query>${RESET} Search components by name/description`)
233
502
  console.log(` ${GREEN}help${RESET} Show this help\n`)
234
503
  console.log(`${BOLD}Usage:${RESET}\n`)
504
+ console.log(` ${DIM}npx @nons-dev/uikit init${RESET}`)
505
+ console.log(` ${DIM}npx @nons-dev/uikit generate${RESET}`)
235
506
  console.log(` ${DIM}npx @nons-dev/uikit list${RESET}`)
236
507
  console.log(` ${DIM}npx @nons-dev/uikit show Button${RESET}`)
237
508
  console.log(` ${DIM}npx @nons-dev/uikit schema Input${RESET}`)
238
- console.log(` ${DIM}npx @nons-dev/uikit search table${RESET}`)
239
- console.log(` ${DIM}npx @nons-dev/uikit init${RESET}`)
240
- console.log(` ${DIM}npx @nons-dev/uikit generate${RESET}\n`)
509
+ console.log(` ${DIM}npx @nons-dev/uikit search table${RESET}\n`)
241
510
  }
242
511
 
243
512
  // ── Main ──