@nons-dev/uikit 0.1.5 → 0.1.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nons-dev/uikit",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "license": "BUSL-1.1",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,6 +20,7 @@
20
20
  "files": [
21
21
  "dist",
22
22
  "generated/components.json",
23
+ "locales",
23
24
  "scripts/cli.mjs",
24
25
  "readme.md"
25
26
  ],
@@ -36,9 +37,8 @@
36
37
  },
37
38
  "scripts": {
38
39
  "dev": "vite",
39
- "build": "npm run generate && vue-tsc -b && vite build && node scripts/copy-fonts.mjs",
40
- "preview": "vite preview",
41
- "generate": "tsx scripts/generate.ts"
40
+ "build": "vue-tsc -b && vite build && node scripts/copy-fonts.mjs",
41
+ "preview": "vite preview"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "vue": "^3.5.0"
@@ -48,10 +48,8 @@
48
48
  "js-yaml": "^5.1.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@types/js-yaml": "^4.0.9",
52
51
  "@types/node": "^26.0.1",
53
52
  "@vitejs/plugin-vue": "^5.2.3",
54
- "tsx": "^4.22.4",
55
53
  "typescript": "~5.7.3",
56
54
  "vite": "^6.3.3",
57
55
  "vite-plugin-dts": "^5.0.3",
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,8 +40,173 @@ font:
41
40
  family: IRANSansX
42
41
  en:
43
42
  family: Satoshi
43
+
44
+ tokens:
45
+ colors:
46
+ light:
47
+ --color-text-default: '#37352F'
48
+ --color-text-grey: '#787774'
49
+ --color-ui-window: '#FFFFFF'
50
+ --color-ui-sidebar: '#F7F6F3'
51
+ --color-ui-hover: rgba(55, 53, 47, 0.08)
52
+ --color-primary: '#9FE870'
53
+ --color-primary-bg: rgba(159, 232, 112, 0.15)
54
+ --color-primary-dark: '#163300'
55
+ --color-primary-text: var(--color-primary-dark)
56
+ --color-primary-selected-bg: rgba(159, 232, 112, 0.08)
57
+ --color-danger: '#F31260'
58
+ --color-danger-bg: '#FEE7EF'
59
+ --color-success: '#17C964'
60
+ --color-success-bg: '#E8FAF0'
61
+ --color-warning: '#F5A524'
62
+ --color-warning-bg: '#FEFCE8'
63
+ --color-secondary: '#7828C8'
64
+ --color-secondary-bg: '#F2EAFA'
65
+ --color-info: '#006FEE'
66
+ --color-info-bg: '#E7F3F8'
67
+ --color-default: '#11181C'
68
+ --color-default-bg: '#F4F4F5'
69
+ --color-border: '#D5D5D2'
70
+ --color-text-secondary: var(--color-text-grey)
71
+ --color-white: '#FFFFFF'
72
+ dark:
73
+ --color-text-default: '#FFFFFF'
74
+ --color-text-grey: '#979A9B'
75
+ --color-ui-window: '#111111'
76
+ --color-ui-sidebar: '#000000'
77
+ --color-ui-hover: rgba(255, 255, 255, 0.08)
78
+ --color-primary: '#9FE870'
79
+ --color-primary-bg: rgba(159, 232, 112, 0.15)
80
+ --color-primary-dark: '#163300'
81
+ --color-primary-text: var(--color-primary-dark)
82
+ --color-primary-selected-bg: rgba(159, 232, 112, 0.08)
83
+ --color-danger: '#f31260'
84
+ --color-danger-bg: '#310413'
85
+ --color-success: '#17c964'
86
+ --color-success-bg: '#052814'
87
+ --color-warning: '#f5a524'
88
+ --color-warning-bg: '#312107'
89
+ --color-secondary: '#9353d3'
90
+ --color-secondary-bg: '#180828'
91
+ --color-info: '#006fee'
92
+ --color-info-bg: '#001731'
93
+ --color-default: '#006fee'
94
+ --color-default-bg: '#001731'
95
+ --color-border: '#1D1D1D'
96
+ --color-text-secondary: var(--color-text-grey)
97
+ --color-white: '#FFFFFF'
98
+
99
+ spacing:
100
+ --space-0-5: 2px
101
+ --space-1: 4px
102
+ --space-2: 8px
103
+ --space-2-5: 10px
104
+ --space-3: 12px
105
+ --space-4: 16px
106
+ --space-5: 24px
107
+ --space-6: 32px
108
+ --space-8: 48px
109
+ --space-10: 64px
110
+ --space-3-5: 14px
111
+ --space-4-5: 18px
112
+ --border-width: 1px
113
+ --border-width-lg: 2px
114
+
115
+ typography:
116
+ --font-family: "'IRANSansX', 'Satoshi', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif"
117
+ --font-family-mono: "'SF Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', monospace"
118
+ --font-size-xs: 12px
119
+ --font-size-sm: 12px
120
+ --font-size-md: 14px
121
+ --font-size-lg: 16px
122
+ --font-size-xl: 18px
123
+ --font-size-2xl: 24px
124
+ --font-weight-normal: 400
125
+ --font-weight-medium: 500
126
+ --font-weight-bold: 700
127
+ --line-height-sm: 18px
128
+ --line-height-md: 22px
129
+ --line-height-lg: 30px
130
+
131
+ radius:
132
+ --radius-sm: 4px
133
+ --radius-md: 8px
134
+ --radius-lg: 12px
135
+ --radius-xl: 16px
136
+ --radius-full: 9999px
137
+
138
+ shadows:
139
+ light:
140
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06)
141
+ --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.08)
142
+ --shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.1)
143
+ dark:
144
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3)
145
+ --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.35)
146
+ --shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.4)
147
+
148
+ z-index:
149
+ --z-dropdown: 100
150
+ --z-popover: 90
151
+ --z-tooltip: 150
152
+ --z-modal: 1000
153
+ --z-panel: 200
154
+
155
+ opacity:
156
+ --opacity-disabled: 0.5
157
+ --opacity-secondary: 0.6
158
+ --opacity-placeholder: 0.6
159
+ --opacity-hint: 0.8
160
+ --opacity-hover: 0.8
161
+
162
+ button:
163
+ --btn-border-radius: 8px
164
+ --btn-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05)
165
+ --btn-padding: 10px 16px
166
+ --btn-font-size: 14px
167
+ --btn-font-weight: 500
168
+ --btn-font-family: "'IRANSansX', sans-serif"
169
+
170
+ alert:
171
+ --alert-border-radius: 16px
172
+ --alert-padding: 12px
173
+ --alert-gap: 16px
174
+
175
+ theme-mappings:
176
+ light:
177
+ --bg-primary: var(--color-ui-window)
178
+ --bg-sidebar: var(--color-ui-sidebar)
179
+ --bg-hover: var(--color-ui-hover)
180
+ --bg-card: var(--color-ui-window)
181
+ --text-primary: var(--color-text-default)
182
+ --text-secondary: var(--color-text-secondary)
183
+ --border-primary: var(--color-border)
184
+ --overlay-bg: rgba(0, 0, 0, 0.4)
185
+ dark:
186
+ --bg-primary: var(--color-ui-window)
187
+ --bg-sidebar: var(--color-ui-sidebar)
188
+ --bg-hover: var(--color-ui-hover)
189
+ --bg-card: '#161816'
190
+ --text-primary: var(--color-text-default)
191
+ --text-secondary: var(--color-text-grey)
192
+ --border-primary: var(--color-border)
193
+ --overlay-bg: rgba(0, 0, 0, 0.6)
44
194
  `
45
195
 
196
+ const LOCALE_FA = JSON.stringify({
197
+ app: { name: 'نونز', footer: 'پنل مدیریت' },
198
+ nav: { dashboard: 'داشبورد', users: 'کاربران', stores: 'فروشگاه‌ها', products: 'محصولات', orders: 'سفارشات', chat: 'گفتگو', reports: 'گزارشات', stats: 'آمار', components: 'کامپوننت‌ها' },
199
+ table: { empty: 'هیچ داده‌ای یافت نشد.' },
200
+ loading: { text: 'در حال بارگذاری...' },
201
+ }, null, 2)
202
+
203
+ const LOCALE_EN = JSON.stringify({
204
+ app: { name: 'Nons', footer: 'Admin Panel' },
205
+ nav: { dashboard: 'Dashboard', users: 'Users', stores: 'Stores', products: 'Products', orders: 'Orders', chat: 'Chat', reports: 'Reports', stats: 'Stats', components: 'Components' },
206
+ table: { empty: 'No data found.' },
207
+ loading: { text: 'Loading...' },
208
+ }, null, 2)
209
+
46
210
  // ── Data ──
47
211
 
48
212
  function loadComponents() {
@@ -73,36 +237,79 @@ function searchComponents(data, query) {
73
237
  // ── Commands ──
74
238
 
75
239
  function cmdInit() {
76
- const dest = path.join(CWD, 'nons.config.yaml')
77
- if (fs.existsSync(dest)) {
240
+ const configDest = path.join(CWD, 'nons.config.yaml')
241
+ if (fs.existsSync(configDest)) {
78
242
  console.log(`${YELLOW}⚠${RESET} nons.config.yaml already exists — skipping.`)
79
243
  return
80
244
  }
81
- fs.writeFileSync(dest, DEFAULT_CONFIG, 'utf-8')
82
- console.log(`${GREEN}✓${RESET} nons.config.yaml created in`, CWD)
245
+ fs.writeFileSync(configDest, DEFAULT_CONFIG, 'utf-8')
246
+ console.log(`${GREEN}✓${RESET} nons.config.yaml created`)
247
+
248
+ const localesDir = path.join(CWD, 'locales')
249
+ if (!fs.existsSync(localesDir)) {
250
+ fs.mkdirSync(localesDir, { recursive: true })
251
+ console.log(`${GREEN}✓${RESET} locales/ directory created`)
252
+ }
253
+
254
+ const faPath = path.join(localesDir, 'fa.json')
255
+ if (!fs.existsSync(faPath)) {
256
+ fs.writeFileSync(faPath, LOCALE_FA, 'utf-8')
257
+ console.log(`${GREEN}✓${RESET} locales/fa.json created`)
258
+ }
259
+
260
+ const enPath = path.join(localesDir, 'en.json')
261
+ if (!fs.existsSync(enPath)) {
262
+ fs.writeFileSync(enPath, LOCALE_EN, 'utf-8')
263
+ console.log(`${GREEN}✓${RESET} locales/en.json created`)
264
+ }
265
+
266
+ console.log(`\n${YELLOW}ℹ${RESET} Install a Vite plugin or adapter to load config automatically:`)
267
+ console.log(` npm install @nons-dev/vite-plugin\n`)
83
268
  }
84
269
 
85
- function cmdGenerate() {
270
+ function cmdValidate() {
86
271
  const configPath = path.join(CWD, 'nons.config.yaml')
87
272
  if (!fs.existsSync(configPath)) {
88
273
  console.error(`${RED}✗${RESET} nons.config.yaml not found. Run \`nons init\` first.`)
89
274
  process.exit(1)
90
275
  }
91
- const raw = fs.readFileSync(configPath, 'utf-8')
92
- const config = yaml.load(raw)
93
- const generatedDir = path.join(CWD, 'generated')
94
- if (!fs.existsSync(generatedDir)) fs.mkdirSync(generatedDir, { recursive: true })
95
-
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};`)
276
+
277
+ try {
278
+ const raw = fs.readFileSync(configPath, 'utf-8')
279
+ const config = yaml.load(raw)
280
+
281
+ const errors = []
282
+ const warnings = []
283
+
284
+ if (!config || typeof config !== 'object') {
285
+ errors.push('Config must be a non-null object')
286
+ } else {
287
+ const c = config
288
+ if (!c.theme || !c.theme.preset) errors.push('Missing theme.preset')
289
+ if (!c.brand || !c.brand.name) errors.push('Missing brand.name')
290
+ if (!c.direction || (c.direction.default !== 'rtl' && c.direction.default !== 'ltr')) errors.push('direction.default must be "rtl" or "ltr"')
291
+ if (!c.locale || !c.locale.default) errors.push('Missing locale.default')
292
+ if (!Array.isArray(c.locale?.supported) || c.locale.supported.length === 0) errors.push('locale.supported must be a non-empty array')
293
+ if (!c.tokens || typeof c.tokens !== 'object') warnings.push('No tokens section found — using defaults')
294
+ }
295
+
296
+ if (errors.length === 0) {
297
+ console.log(`\n${GREEN}✓${RESET} Config is valid\n`)
298
+ } else {
299
+ console.log(`\n${RED}✗${RESET} Config has errors:\n`)
300
+ for (const e of errors) console.log(` ${RED}•${RESET} ${e}`)
301
+ console.log('')
302
+ }
303
+ if (warnings.length) {
304
+ for (const w of warnings) console.log(` ${YELLOW}•${RESET} ${w}`)
305
+ console.log('')
306
+ }
307
+
308
+ if (errors.length) process.exit(1)
309
+ } catch (err) {
310
+ console.error(`\n${RED}✗${RESET} Invalid YAML: ${err.message}\n`)
311
+ process.exit(1)
102
312
  }
103
- lines.push('}')
104
- fs.writeFileSync(path.join(generatedDir, 'tokens.css'), lines.join('\n'), 'utf-8')
105
- console.log(`${GREEN}✓${RESET} generated/tokens.css`)
106
313
  }
107
314
 
108
315
  function cmdList() {
@@ -224,20 +431,20 @@ function cmdSearch(query) {
224
431
  function printHelp() {
225
432
  console.log(`\n${BOLD}${CYAN}Nons UIKit CLI${RESET}\n`)
226
433
  console.log(`${BOLD}Commands:${RESET}\n`)
227
- console.log(` ${GREEN}init${RESET} Create nons.config.yaml in current directory`)
228
- console.log(` ${GREEN}generate${RESET} Generate CSS from nons.config.yaml`)
434
+ console.log(` ${GREEN}init${RESET} Create nons.config.yaml + locales/ in current directory`)
435
+ console.log(` ${GREEN}validate${RESET} Validate nons.config.yaml`)
229
436
  console.log(` ${GREEN}list${RESET} List all components`)
230
437
  console.log(` ${GREEN}show${RESET} ${DIM}<name>${RESET} Show full detail for a component`)
231
438
  console.log(` ${GREEN}schema${RESET} ${DIM}<name>${RESET} Show schema JSON for a component`)
232
439
  console.log(` ${GREEN}search${RESET} ${DIM}<query>${RESET} Search components by name/description`)
233
440
  console.log(` ${GREEN}help${RESET} Show this help\n`)
234
441
  console.log(`${BOLD}Usage:${RESET}\n`)
442
+ console.log(` ${DIM}npx @nons-dev/uikit init${RESET}`)
443
+ console.log(` ${DIM}npx @nons-dev/uikit validate${RESET}`)
235
444
  console.log(` ${DIM}npx @nons-dev/uikit list${RESET}`)
236
445
  console.log(` ${DIM}npx @nons-dev/uikit show Button${RESET}`)
237
446
  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`)
447
+ console.log(` ${DIM}npx @nons-dev/uikit search table${RESET}\n`)
241
448
  }
242
449
 
243
450
  // ── Main ──
@@ -247,7 +454,7 @@ const param = process.argv[3]
247
454
 
248
455
  switch (command) {
249
456
  case 'init': cmdInit(); break
250
- case 'generate': cmdGenerate(); break
457
+ case 'validate': cmdValidate(); break
251
458
  case 'list': cmdList(); break
252
459
  case 'show': param ? cmdShow(param) : (console.error(`${RED}✗${RESET} Missing component name.`), process.exit(1)); break
253
460
  case 'schema': param ? cmdSchema(param) : (console.error(`${RED}✗${RESET} Missing component name.`), process.exit(1)); break