rizzo-css 0.0.22 → 0.0.24

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.
Files changed (35) hide show
  1. package/README.md +4 -4
  2. package/bin/rizzo-css.js +244 -48
  3. package/package.json +1 -1
  4. package/scaffold/astro/Navbar.astro +7 -0
  5. package/scaffold/astro/Settings.astro +374 -5
  6. package/scaffold/astro-minimal/{RIZZO-README.md → README-RIZZO.md} +2 -2
  7. package/scaffold/astro-minimal/src/layouts/Layout.astro +1 -2
  8. package/scaffold/svelte-minimal/{RIZZO-README.md → README-RIZZO.md} +4 -4
  9. package/scaffold/vanilla/{RIZZO-README.md → README-RIZZO.md} +3 -3
  10. package/scaffold/vanilla/components/accordion.html +12 -0
  11. package/scaffold/vanilla/components/alert.html +12 -0
  12. package/scaffold/vanilla/components/avatar.html +12 -0
  13. package/scaffold/vanilla/components/badge.html +12 -0
  14. package/scaffold/vanilla/components/breadcrumb.html +12 -0
  15. package/scaffold/vanilla/components/button.html +12 -0
  16. package/scaffold/vanilla/components/cards.html +12 -0
  17. package/scaffold/vanilla/components/copy-to-clipboard.html +12 -0
  18. package/scaffold/vanilla/components/divider.html +12 -0
  19. package/scaffold/vanilla/components/dropdown.html +12 -0
  20. package/scaffold/vanilla/components/forms.html +12 -0
  21. package/scaffold/vanilla/components/icons.html +12 -0
  22. package/scaffold/vanilla/components/index.html +12 -0
  23. package/scaffold/vanilla/components/modal.html +12 -0
  24. package/scaffold/vanilla/components/navbar.html +12 -0
  25. package/scaffold/vanilla/components/pagination.html +12 -0
  26. package/scaffold/vanilla/components/progress-bar.html +12 -0
  27. package/scaffold/vanilla/components/search.html +12 -0
  28. package/scaffold/vanilla/components/settings.html +12 -0
  29. package/scaffold/vanilla/components/spinner.html +12 -0
  30. package/scaffold/vanilla/components/table.html +12 -0
  31. package/scaffold/vanilla/components/tabs.html +12 -0
  32. package/scaffold/vanilla/components/theme-switcher.html +12 -0
  33. package/scaffold/vanilla/components/toast.html +12 -0
  34. package/scaffold/vanilla/components/tooltip.html +12 -0
  35. package/scaffold/vanilla/index.html +12 -0
package/README.md CHANGED
@@ -12,7 +12,7 @@ pnpm add rizzo-css
12
12
  yarn add rizzo-css
13
13
  ```
14
14
 
15
- **Quick start (no install):** `npx rizzo-css init` — choose **framework** (Vanilla, Astro, or Svelte), then **add to existing** or **create new**. **Existing** (or `npx rizzo-css add`) → drop in CSS + hand-pick components; you must add the `<link>` yourself (CLI prints the exact tag). **New** → choose **Full** (everything) | **Minimal** (recommended) | **Manual** (pick components), then package manager. Non-interactive: `npx rizzo-css init --yes --framework vanilla|astro|svelte`. Optional **rizzo-css.json** and `add --install-package`. All get the **same CSS and component styles**. To use the **official Svelte or Astro create command** plus Rizzo, create the app first, then run `npx rizzo-css add`:
15
+ **Quick start (no install):** `npx rizzo-css init` — choose **framework** (Vanilla, Astro, or Svelte), then **add to existing** or **create new**. **Existing** (or `npx rizzo-css add`) → drop in CSS + hand-pick components; you must add the `<link>` yourself (CLI prints the exact tag). **New** → choose **Full** (everything) | **Minimal** (recommended) | **Manual** (pick components; list shows which add others, e.g. Settings adds ThemeSwitcher), then package manager. Full and Minimal include all required dependencies so every component works. Run `npx rizzo-css help components` for the dependency list. Non-interactive: `npx rizzo-css init --yes --framework vanilla|astro|svelte`. Optional **rizzo-css.json** and `add --install-package`. All get the **same CSS and component styles**. To use the **official Svelte or Astro create command** plus Rizzo, create the app first, then run `npx rizzo-css add`:
16
16
 
17
17
  ```bash
18
18
  npm create svelte@latest my-app && cd my-app && npx rizzo-css add
@@ -41,7 +41,7 @@ You install **the same package** for every framework: `npm install rizzo-css`. N
41
41
 
42
42
  With `npx rizzo-css add --path <dir>`, the CLI still suggests the correct href for your framework (e.g. Astro/Svelte get a leading `/` path).
43
43
 
44
- Scaffolds in the package: `scaffold/vanilla/` (Full or Manual), `scaffold/astro-minimal/`, `scaffold/svelte-minimal/`, plus `scaffold/astro/` and `scaffold/svelte/` (component templates for hand-pick). Use `npx rizzo-css init` and choose **Create new project** to get a **Full** or **Manual** scaffold; the stylesheet link is in the layout. **Add to existing** (or `add` command) drops in CSS + hand-pick components; **you must add the stylesheet `<link>` yourself** — the CLI prints the exact tag. Every scaffold includes RIZZO-LICENSE and RIZZO-README.md (does not overwrite your project LICENSE/README); Astro/Svelte minimal include package.json and .env.example.
44
+ Scaffolds in the package: `scaffold/vanilla/` (Full or Manual), `scaffold/astro-minimal/`, `scaffold/svelte-minimal/`, plus `scaffold/astro/` and `scaffold/svelte/` (component templates for hand-pick). Use `npx rizzo-css init` and choose **Create new project** to get a **Full** or **Manual** scaffold; the stylesheet link is in the layout. **Add to existing** (or `add` command) drops in CSS + hand-pick components; **you must add the stylesheet `<link>` yourself** — the CLI prints the exact tag. Every scaffold includes LICENSE-RIZZO and README-RIZZO.md (does not overwrite your project LICENSE/README); Astro/Svelte minimal include package.json and .env.example.
45
45
 
46
46
  ## Use
47
47
 
@@ -56,7 +56,7 @@ import 'rizzo-css';
56
56
  **Without a bundler (plain HTML):** Use a CDN. Both unpkg and jsDelivr resolve the package root to the built CSS (via the `unpkg` / `jsdelivr` fields in this package). For reliability or to pin a version, use the explicit path:
57
57
 
58
58
  ```html
59
- <!-- unpkg (pin version: replace @latest with @0.0.22 or any version) -->
59
+ <!-- unpkg (pin version: replace @latest with @0.0.24 or any version) -->
60
60
  <link rel="stylesheet" href="https://unpkg.com/rizzo-css@latest/dist/rizzo.min.css" />
61
61
 
62
62
  <!-- or jsDelivr -->
@@ -65,7 +65,7 @@ import 'rizzo-css';
65
65
 
66
66
  Short URLs also work: `https://unpkg.com/rizzo-css@latest` and `https://cdn.jsdelivr.net/npm/rizzo-css@latest` (CDNs serve the default file from package.json). To verify after publish: open the URL in a browser or run `curl -I https://unpkg.com/rizzo-css@latest/dist/rizzo.min.css` and expect `200 OK`.
67
67
 
68
- Use the same class names and HTML structure as in the [component docs](https://rizzo-css.vercel.app/docs/components). **Vanilla JS**, Astro, and Svelte all use the same CSS and BEM markup; Astro/Svelte add framework component files when you hand-pick. Each scaffold has RIZZO-README.md; every install includes RIZZO-LICENSE. The **Navbar** component in the scaffold includes the default Cat logo in the brand link (optional `logo` prop for a custom image). The **Vanilla** Full includes a Settings panel and toast; **Astro** and **Svelte** Full scaffolds include theme persistence and toast (`showToast`, `removeToast`, `removeAllToasts`).
68
+ Use the same class names and HTML structure as in the [component docs](https://rizzo-css.vercel.app/docs/components). **Vanilla JS**, Astro, and Svelte all use the same CSS and BEM markup; Astro/Svelte add framework component files when you hand-pick. Each scaffold has README-RIZZO.md; every install includes LICENSE-RIZZO. The **Navbar** component in the scaffold includes the default Cat logo in the brand link (optional `logo` prop for a custom image). The **Vanilla** Full includes a Settings panel and toast; **Astro** and **Svelte** Full scaffolds include theme persistence and toast (`showToast`, `removeToast`, `removeAllToasts`).
69
69
 
70
70
  ## Themes
71
71
 
package/bin/rizzo-css.js CHANGED
@@ -7,9 +7,9 @@ const readline = require('readline');
7
7
 
8
8
  const RIZZO_CONFIG_FILE = 'rizzo-css.json';
9
9
  /** Scaffold README filename; avoids overwriting an existing project README.md. */
10
- const SCAFFOLD_README_FILENAME = 'RIZZO-README.md';
10
+ const SCAFFOLD_README_FILENAME = 'README-RIZZO.md';
11
11
  /** Scaffold license filename; avoids overwriting an existing project LICENSE. */
12
- const SCAFFOLD_LICENSE_FILENAME = 'RIZZO-LICENSE';
12
+ const SCAFFOLD_LICENSE_FILENAME = 'LICENSE-RIZZO';
13
13
 
14
14
  const COMMANDS = ['init', 'add', 'theme', 'help'];
15
15
  const FRAMEWORKS = ['vanilla', 'astro', 'svelte'];
@@ -24,14 +24,14 @@ const TEMPLATES = {
24
24
  { value: 'manual', label: 'Manual — index.html + CSS; pick components to add their pages + js/main.js' },
25
25
  ],
26
26
  astro: [
27
- { value: 'full', label: 'Full — Astro app + all 25 components (config, one page, ' + SCAFFOLD_README_FILENAME + ', ' + SCAFFOLD_LICENSE_FILENAME + ', .env.example)' },
28
- { value: 'minimal', label: 'Minimal — Astro app + recommended components (Button, Badge, Card, Modal, Tabs, ThemeSwitcher, FormGroup, Alert, Toast, Dropdown)' },
29
- { value: 'manual', label: 'Manual — minimal base + pick the components you want' },
27
+ { value: 'full', label: 'Full — Astro app + all components (with dependencies so everything works)' },
28
+ { value: 'minimal', label: 'Minimal — Astro app + recommended set (includes any required dependencies)' },
29
+ { value: 'manual', label: 'Manual — minimal base + pick components (list shows which add others)' },
30
30
  ],
31
31
  svelte: [
32
- { value: 'full', label: 'Full — SvelteKit app + all 25 components (config, one page, ' + SCAFFOLD_README_FILENAME + ', ' + SCAFFOLD_LICENSE_FILENAME + ', .env.example)' },
33
- { value: 'minimal', label: 'Minimal — SvelteKit app + recommended components (Button, Badge, Card, Modal, Tabs, ThemeSwitcher, FormGroup, Alert, Toast, Dropdown)' },
34
- { value: 'manual', label: 'Manual — minimal base + pick the components you want' },
32
+ { value: 'full', label: 'Full — SvelteKit app + all components (with dependencies so everything works)' },
33
+ { value: 'minimal', label: 'Minimal — SvelteKit app + recommended set (includes any required dependencies)' },
34
+ { value: 'manual', label: 'Manual — minimal base + pick components (list shows which add others)' },
35
35
  ],
36
36
  };
37
37
 
@@ -112,6 +112,61 @@ const RECOMMENDED_COMPONENTS = [
112
112
  'Button', 'Badge', 'Card', 'Modal', 'Tabs', 'ThemeSwitcher', 'FormGroup', 'Alert', 'Toast', 'Dropdown',
113
113
  ];
114
114
 
115
+ // Component dependencies per framework: when user selects a component, these are copied automatically so it works.
116
+ // Manual users can run: npx rizzo-css help components
117
+ const COMPONENT_DEPS = {
118
+ astro: { Settings: ['ThemeSwitcher'], Toast: ['Alert'] },
119
+ svelte: { Settings: ['ThemeSwitcher'], Toast: ['Alert'] },
120
+ };
121
+
122
+ function getComponentDeps(framework, componentName) {
123
+ const deps = COMPONENT_DEPS[framework];
124
+ return (deps && deps[componentName]) ? deps[componentName] : [];
125
+ }
126
+
127
+ /** Returns a short label for the picker, e.g. " (adds ThemeSwitcher)" or "". */
128
+ function getComponentDependencyLabel(framework, componentName) {
129
+ const deps = getComponentDeps(framework, componentName);
130
+ if (deps.length === 0) return '';
131
+ return ' (adds ' + deps.join(', ') + ')';
132
+ }
133
+
134
+ /** Expand a list of component names with all required dependencies. Used for full, minimal, and add so everything works. */
135
+ function expandWithDeps(framework, names) {
136
+ const depsMap = COMPONENT_DEPS[framework];
137
+ if (!depsMap) return [...names];
138
+ const out = new Set(names);
139
+ let added = true;
140
+ while (added) {
141
+ added = false;
142
+ for (const name of out) {
143
+ const req = depsMap[name];
144
+ if (req) for (const d of req) {
145
+ if (!out.has(d)) { out.add(d); added = true; }
146
+ }
147
+ }
148
+ }
149
+ return [...names].filter((n) => out.has(n)).concat([...out].filter((n) => !names.includes(n)));
150
+ }
151
+
152
+ /** Log which components were added automatically because others require them. Call before copy when expanded.length > selected.length. */
153
+ function logAddedDeps(selected, expanded, framework) {
154
+ const added = expanded.filter((n) => !selected.includes(n));
155
+ if (added.length === 0) return;
156
+ const depsMap = COMPONENT_DEPS[framework];
157
+ if (!depsMap) return;
158
+ const byRequirement = [];
159
+ for (const name of added) {
160
+ for (const [parent, deps] of Object.entries(depsMap)) {
161
+ if (deps.includes(name) && selected.includes(parent)) {
162
+ byRequirement.push(name + ' (required by ' + parent + ')');
163
+ break;
164
+ }
165
+ }
166
+ }
167
+ if (byRequirement.length) console.log('\n Also adding: ' + byRequirement.join('; '));
168
+ }
169
+
115
170
  // Vanilla scaffold: component name (same as ASTRO_COMPONENTS) -> components/*.html slug. Navbar, Settings, Search, Icons are vanilla-only.
116
171
  const VANILLA_COMPONENT_SLUGS = {
117
172
  Button: 'button', Badge: 'badge', Card: 'cards', Divider: 'divider', Spinner: 'spinner', ProgressBar: 'progress-bar',
@@ -185,7 +240,30 @@ function copyRizzoCssAndFontsForAstro(projectDir, cssSource) {
185
240
  }
186
241
  }
187
242
 
188
- /** Copy the package LICENSE into the project dir as RIZZO-LICENSE so we do not overwrite an existing LICENSE. */
243
+ /** SvelteKit only: copy rizzo.min.css to static/css with font URLs rewritten to /assets/fonts/, and copy fonts to static/assets/fonts. */
244
+ function copyRizzoCssAndFontsForSvelte(projectDir, cssSource) {
245
+ const cssDir = join(projectDir, 'static', 'css');
246
+ const cssTarget = join(cssDir, 'rizzo.min.css');
247
+ const fontsDest = join(projectDir, 'static', 'assets', 'fonts');
248
+ mkdirSync(cssDir, { recursive: true });
249
+ mkdirSync(fontsDest, { recursive: true });
250
+ copyFileSync(cssSource, cssTarget);
251
+ let css = readFileSync(cssTarget, 'utf8');
252
+ css = css.replace(/url\(['"]?\.\/fonts\//g, "url('/assets/fonts/");
253
+ writeFileSync(cssTarget, css, 'utf8');
254
+ const fontsSrc = join(getPackageRoot(), 'dist', 'fonts');
255
+ if (existsSync(fontsSrc)) {
256
+ const entries = readdirSync(fontsSrc, { withFileTypes: true });
257
+ for (const e of entries) {
258
+ const srcPath = join(fontsSrc, e.name);
259
+ const destPath = join(fontsDest, e.name);
260
+ if (e.isDirectory()) copyDirRecursive(srcPath, destPath);
261
+ else copyFileSync(srcPath, destPath);
262
+ }
263
+ }
264
+ }
265
+
266
+ /** Copy the package LICENSE into the project dir as LICENSE-RIZZO so we do not overwrite an existing LICENSE. */
189
267
  function copyPackageLicense(projectDir) {
190
268
  const licensePath = join(getPackageRoot(), 'LICENSE');
191
269
  if (existsSync(licensePath)) {
@@ -208,11 +286,17 @@ function readRizzoConfig(cwd) {
208
286
  } catch (_) { return null; }
209
287
  }
210
288
 
211
- /** Write rizzo-css.json to cwd. config: { targetDir?, framework?, packageManager? }. */
289
+ /** Write rizzo-css.json to cwd. config: { targetDir?, framework?, packageManager? }. Preserves unknown keys from existing file. */
212
290
  function writeRizzoConfig(cwd, config) {
213
291
  if (!cwd || !existsSync(cwd)) return;
214
292
  const configPath = join(cwd, RIZZO_CONFIG_FILE);
215
- const obj = {};
293
+ let obj = {};
294
+ if (existsSync(configPath)) {
295
+ try {
296
+ const raw = JSON.parse(readFileSync(configPath, 'utf8'));
297
+ if (raw && typeof raw === 'object') obj = { ...raw };
298
+ } catch (_) { /* ignore */ }
299
+ }
216
300
  if (config.targetDir != null) obj.targetDir = config.targetDir;
217
301
  if (config.framework != null) obj.framework = config.framework;
218
302
  if (config.packageManager != null) obj.packageManager = config.packageManager;
@@ -366,9 +450,10 @@ function selectMenu(options, title) {
366
450
  });
367
451
  }
368
452
 
453
+ const HINT = ' \u2191\u2193 move \u00b7 Enter select \u00b7 1-9 pick';
369
454
  return new Promise((resolve) => {
370
455
  let index = 0;
371
- const lineCount = (title ? 1 : 0) + items.length + 1;
456
+ const lineCount = (title ? 1 : 0) + items.length + 2; // +1 blank +1 hint
372
457
 
373
458
  const render = (first) => {
374
459
  const lines = (title ? [title] : []).concat(
@@ -377,15 +462,25 @@ function selectMenu(options, title) {
377
462
  const prefix = i === index ? C.cyan + '>' + C.reset + ' ' : ' ';
378
463
  return prefix + circle + formatLabel(item);
379
464
  })
380
- );
465
+ ).concat(['', HINT]);
381
466
  if (!first) {
382
467
  process.stdout.write('\u001b[' + lineCount + 'A');
383
468
  }
384
469
  process.stdout.write('\u001b[?25l');
385
- process.stdout.write(lines.join('\n') + '\n\n');
470
+ process.stdout.write(lines.join('\n') + '\n');
386
471
  process.stdout.write('\u001b[?25h');
387
472
  };
388
473
 
474
+ const selectByNumber = (num) => {
475
+ const n = num === 0 ? 10 : num; // 0 = 10th item
476
+ const idx = n >= 1 && n <= items.length ? n - 1 : index;
477
+ process.stdin.setRawMode(false);
478
+ process.stdin.removeListener('data', onData);
479
+ process.stdin.pause();
480
+ process.stdout.write('\n');
481
+ resolve(items[idx].value);
482
+ };
483
+
389
484
  process.stdin.setRawMode(true);
390
485
  process.stdin.resume();
391
486
  process.stdin.setEncoding('utf8');
@@ -408,6 +503,16 @@ function selectMenu(options, title) {
408
503
  resolve(items[index].value);
409
504
  return;
410
505
  }
506
+ if (ch >= '1' && ch <= '9') {
507
+ buf = '';
508
+ selectByNumber(parseInt(ch, 10));
509
+ return;
510
+ }
511
+ if (ch === '0' && items.length >= 10) {
512
+ buf = '';
513
+ selectByNumber(10);
514
+ return;
515
+ }
411
516
  buf += ch;
412
517
  const isUp = buf === '\u001b[A' || buf === '\u001bOA' || (buf.length >= 2 && buf.endsWith('A') && buf.startsWith('\u001b'));
413
518
  const isDown = buf === '\u001b[B' || buf === '\u001bOB' || (buf.length >= 2 && buf.endsWith('B') && buf.startsWith('\u001b'));
@@ -499,7 +604,8 @@ function multiSelectMenu(options, title, initialSelected) {
499
604
  if (initialSet.has(items[i].value)) selected.add(i);
500
605
  }
501
606
  }
502
- const lineCount = (title ? 1 : 0) + items.length + 1;
607
+ const HINT = ' \u2191\u2193 move \u00b7 Space toggle \u00b7 a all \u00b7 n none \u00b7 Enter confirm';
608
+ const lineCount = (title ? 1 : 0) + items.length + 2; // +1 blank +1 hint
503
609
  const realStart = withSentinels ? 2 : 0;
504
610
 
505
611
  const finish = () => {
@@ -527,15 +633,24 @@ function multiSelectMenu(options, title, initialSelected) {
527
633
  const prefix = i === index ? C.cyan + '>' + C.reset + ' ' : ' ';
528
634
  return prefix + circle + formatLabel(item);
529
635
  })
530
- );
636
+ ).concat(['', HINT]);
531
637
  if (!first) {
532
638
  process.stdout.write('\u001b[' + lineCount + 'A');
533
639
  }
534
640
  process.stdout.write('\u001b[?25l');
535
- process.stdout.write(lines.join('\n') + '\n\n');
641
+ process.stdout.write(lines.join('\n') + '\n');
536
642
  process.stdout.write('\u001b[?25h');
537
643
  };
538
644
 
645
+ const toggleByNumber = (num) => {
646
+ const n = num === 0 ? 10 : num;
647
+ const idx = n >= 1 && n <= items.length ? n - 1 : -1;
648
+ if (idx < realStart) return; // don't toggle "Select all" / "Select none"
649
+ if (selected.has(idx)) selected.delete(idx);
650
+ else selected.add(idx);
651
+ render(false);
652
+ };
653
+
539
654
  process.stdin.setRawMode(true);
540
655
  process.stdin.resume();
541
656
  process.stdin.setEncoding('utf8');
@@ -576,6 +691,16 @@ function multiSelectMenu(options, title, initialSelected) {
576
691
  render(false);
577
692
  return;
578
693
  }
694
+ if (ch >= '1' && ch <= '9') {
695
+ buf = '';
696
+ toggleByNumber(parseInt(ch, 10));
697
+ return;
698
+ }
699
+ if (ch === '0' && items.length >= 10) {
700
+ buf = '';
701
+ toggleByNumber(10);
702
+ return;
703
+ }
579
704
  if (ch === 'a' || ch === 'A') {
580
705
  buf = '';
581
706
  for (let i = realStart; i < items.length; i++) selected.add(i);
@@ -611,6 +736,14 @@ function printHelp() {
611
736
  console.log(`
612
737
  rizzo-css CLI — Add Rizzo CSS to your project (Vanilla, Astro, Svelte)
613
738
 
739
+ Available commands: init, add, theme, help
740
+
741
+ Flags summary:
742
+ init --yes --framework <fw> --template <t> --package-manager <pm> --install --no-install
743
+ add --path <dir> --framework <fw> --package-manager <pm> --install-package --no-install
744
+ theme (no flags)
745
+ help (no flags)
746
+
614
747
  Usage (use your package manager):
615
748
  npx rizzo-css <command> [options]
616
749
  pnpm dlx rizzo-css <command> [options]
@@ -642,6 +775,11 @@ Options (add):
642
775
  Package managers:
643
776
  Supported: npm, pnpm, yarn, bun. Detection: lockfiles (pnpm-lock.yaml, yarn.lock, bun.lockb, package-lock.json) or package.json "packageManager"/"devEngines.packageManager". Use --package-manager to override.
644
777
 
778
+ Interactive prompts (when no --yes/flag provided):
779
+ Single-choice: ↑/↓ move, Enter select, 1-9 pick by number (0 = 10th).
780
+ Multi-choice: ↑/↓ move, Space toggle, a = all, n = none, Enter confirm, 1-9 toggle by number.
781
+ Ctrl+C to exit.
782
+
645
783
  Config:
646
784
  Optional rizzo-css.json in project root: { "targetDir", "framework", "packageManager" }.
647
785
  Used by add and init when present. Detection: lockfiles and packageManager field in package.json.
@@ -670,10 +808,43 @@ Examples:
670
808
  npx rizzo-css add --install-package
671
809
  npx rizzo-css theme
672
810
 
811
+ Component dependencies (manual / add):
812
+ Some components require others to work. Picking them adds the required ones automatically.
813
+ Full list of available components and what relies on what: npx rizzo-css help components
814
+
673
815
  Docs: https://rizzo-css.vercel.app
674
816
  `);
675
817
  }
676
818
 
819
+ function printHelpComponents() {
820
+ const list = ASTRO_COMPONENTS.map((name) => {
821
+ const deps = getComponentDeps('astro', name);
822
+ return deps.length ? name + ' (adds ' + deps.join(', ') + ')' : name;
823
+ });
824
+ const line1 = list.slice(0, 10).join(', ');
825
+ const line2 = list.slice(10, 20).join(', ');
826
+ const line3 = list.slice(20).join(', ');
827
+ console.log(`
828
+ Components — full list and what relies on what
829
+
830
+ Available to pick (Astro & Svelte; same list):
831
+ ` + line1 + (line2 ? ',\n ' + line2 : '') + (line3 ? ',\n ' + line3 : '') + `
832
+
833
+ Dependencies (when you pick the component on the left, the right is added automatically):
834
+ Settings → ThemeSwitcher (and themes.ts)
835
+ Toast → Alert
836
+
837
+ ThemeSwitcher and ThemeIcon: when selected, themes.ts (and Svelte theme.ts) is copied so they work.
838
+ Icons: copied whenever you add any component.
839
+
840
+ Full = all components above; dependencies are included so everything works.
841
+ Minimal = recommended set; any component in that set that requires others gets them.
842
+ Manual = you pick; the picker shows e.g. "Settings (adds ThemeSwitcher)". Required deps are added when you confirm.
843
+
844
+ To see this again: npx rizzo-css help components
845
+ `);
846
+ }
847
+
677
848
  function cmdTheme() {
678
849
  process.stdout.write('\nDark themes (set data-theme on <html>):\n');
679
850
  DARK_THEMES.forEach((t) => process.stdout.write(' ' + t + '\n'));
@@ -704,6 +875,11 @@ async function promptThemes() {
704
875
  return { theme, defaultDark: DARK_THEMES.includes(defaultDark) ? defaultDark : DARK_THEMES[0], defaultLight: LIGHT_THEMES.includes(defaultLight) ? defaultLight : LIGHT_THEMES[0] };
705
876
  }
706
877
 
878
+ function componentOptionLabel(framework, name) {
879
+ const suffix = getComponentDependencyLabel(framework, name);
880
+ return name + suffix;
881
+ }
882
+
707
883
  /** Ask what to include: CSS only, recommended set, all components, or pick. Returns array of component names. Only call when componentList.length > 0. initialSelection: when set (e.g. for manual = minimal base), skip the menu and show multi-select with these pre-selected. */
708
884
  async function promptComponentChoice(componentList, framework, initialSelection) {
709
885
  const recommended = RECOMMENDED_COMPONENTS.filter((c) => componentList.includes(c));
@@ -712,7 +888,7 @@ async function promptComponentChoice(componentList, framework, initialSelection)
712
888
  [
713
889
  { value: SENTINEL_ALL, label: 'Select all components' },
714
890
  { value: SENTINEL_NONE, label: 'Select no components' },
715
- ...componentList.map((c) => ({ value: c, label: c })),
891
+ ...componentList.map((c) => ({ value: c, label: componentOptionLabel(framework, c) })),
716
892
  ],
717
893
  '? Components (minimal set pre-selected) — Space to toggle, Enter to confirm',
718
894
  initialSelection
@@ -734,9 +910,9 @@ async function promptComponentChoice(componentList, framework, initialSelection)
734
910
  [
735
911
  { value: SENTINEL_ALL, label: 'Select all components' },
736
912
  { value: SENTINEL_NONE, label: 'Select no components' },
737
- ...componentList.map((c) => ({ value: c, label: c })),
913
+ ...componentList.map((c) => ({ value: c, label: componentOptionLabel(framework, c) })),
738
914
  ],
739
- '? Components — Space to toggle, Enter to confirm'
915
+ '? Components — Space to toggle, Enter to confirm. Items like "Settings (adds ThemeSwitcher)" add those deps automatically. List: npx rizzo-css help components'
740
916
  );
741
917
  }
742
918
 
@@ -757,12 +933,12 @@ function detectFramework(cwd) {
757
933
  /**
758
934
  * Framework-specific paths for CSS and static assets. Use these so fonts, sounds, images
759
935
  * go in the right place per framework (Astro: public/, SvelteKit: static/, Vanilla: project root).
760
- * - targetDir: where rizzo.min.css is copied. Fonts: for Astro, public/assets/fonts (CLI rewrites URLs); for Svelte/Vanilla, targetDir/fonts so ./fonts/ resolves.
761
- * - assetsRoot: root for static assets (Astro: public; use public/assets/fonts, public/assets/sounds).
936
+ * - targetDir: where rizzo.min.css is copied. Fonts: Astro public/assets/fonts, Svelte static/assets/fonts (CLI rewrites URLs); Vanilla targetDir/fonts.
937
+ * - assetsRoot: root for static assets (Astro: public; Svelte: static; Vanilla: '').
762
938
  */
763
939
  function getFrameworkCssPaths(framework) {
764
940
  if (framework === 'svelte') {
765
- return { targetDir: 'static/css', linkHref: '/css/rizzo.min.css', fontsDir: 'static/css/fonts', assetsRoot: 'static' };
941
+ return { targetDir: 'static/css', linkHref: '/css/rizzo.min.css', fontsDir: 'static/assets/fonts', assetsRoot: 'static' };
766
942
  }
767
943
  if (framework === 'astro') {
768
944
  return { targetDir: 'public/css', linkHref: '/css/rizzo.min.css', fontsDir: 'public/assets/fonts', assetsRoot: 'public' };
@@ -990,7 +1166,7 @@ function copySvelteComponents(projectDir, selectedNames) {
990
1166
  if (existsSync(iconsSrc) && (toCopy.length > 0 || copyIconsOnly)) {
991
1167
  copyDirRecursive(iconsSrc, join(targetDir, 'icons'));
992
1168
  }
993
- if (toCopy.includes('ThemeSwitcher')) {
1169
+ if (toCopy.includes('ThemeSwitcher') || toCopy.includes('ThemeIcon')) {
994
1170
  const themesSrc = join(scaffoldDir, 'themes.ts');
995
1171
  const themeSrc = join(scaffoldDir, 'theme.ts');
996
1172
  if (existsSync(themesSrc)) copyFileSync(themesSrc, join(targetDir, 'themes.ts'));
@@ -1035,7 +1211,7 @@ function copyAstroComponents(projectDir, selectedNames) {
1035
1211
  if (existsSync(iconsSrc) && (toCopy.length > 0 || copyIconsOnly)) {
1036
1212
  copyDirRecursive(iconsSrc, join(targetDir, 'icons'));
1037
1213
  }
1038
- if (toCopy.includes('ThemeSwitcher')) {
1214
+ if (toCopy.includes('ThemeSwitcher') || toCopy.includes('ThemeIcon')) {
1039
1215
  const themesSrc = join(scaffoldDir, 'themes.ts');
1040
1216
  if (existsSync(themesSrc)) {
1041
1217
  copyFileSync(themesSrc, join(targetDir, 'themes.ts'));
@@ -1127,6 +1303,9 @@ async function runAddToExisting(frameworkOverride, options) {
1127
1303
  if (framework === 'astro') {
1128
1304
  copyRizzoCssAndFontsForAstro(cwd, cssSource);
1129
1305
  cssTarget = join(cwd, 'public', 'css', 'rizzo.min.css');
1306
+ } else if (framework === 'svelte') {
1307
+ copyRizzoCssAndFontsForSvelte(cwd, cssSource);
1308
+ cssTarget = join(cwd, 'static', 'css', 'rizzo.min.css');
1130
1309
  } else {
1131
1310
  const targetDir = join(cwd, targetDirRaw);
1132
1311
  cssTarget = join(targetDir, 'rizzo.min.css');
@@ -1137,16 +1316,20 @@ async function runAddToExisting(frameworkOverride, options) {
1137
1316
 
1138
1317
  copyRizzoIcons(cwd, framework);
1139
1318
  if (framework === 'svelte' && selectedComponents.length > 0) {
1140
- copySvelteComponents(cwd, selectedComponents);
1319
+ const expanded = expandWithDeps('svelte', selectedComponents);
1320
+ logAddedDeps(selectedComponents, expanded, 'svelte');
1321
+ copySvelteComponents(cwd, expanded);
1141
1322
  } else if (framework === 'astro' && selectedComponents.length > 0) {
1142
- copyAstroComponents(cwd, selectedComponents);
1323
+ const expanded = expandWithDeps('astro', selectedComponents);
1324
+ logAddedDeps(selectedComponents, expanded, 'astro');
1325
+ copyAstroComponents(cwd, expanded);
1143
1326
  } else if (framework === 'vanilla' && selectedComponents.length > 0) {
1144
1327
  const linkHrefForVanilla = (options && options.targetDir) ? getLinkHrefForTargetDir(framework, options.targetDir) : paths.linkHref;
1145
1328
  const vanillaRepl = { '{{LINK_HREF}}': linkHrefForVanilla, '{{DATA_THEME}}': theme };
1146
1329
  copyVanillaComponents(cwd, selectedComponents, vanillaRepl);
1147
1330
  }
1148
1331
 
1149
- const linkHref = (options && options.targetDir) ? getLinkHrefForTargetDir(framework, options.targetDir) : paths.linkHref;
1332
+ const linkHref = (framework === 'astro' || framework === 'svelte') ? paths.linkHref : ((options && options.targetDir) ? getLinkHrefForTargetDir(framework, options.targetDir) : paths.linkHref);
1150
1333
  const pmFromOption = options && options.packageManager && VALID_PACKAGE_MANAGERS.includes(options.packageManager);
1151
1334
  const pm = pmFromOption
1152
1335
  ? getPackageManagerCommands({ agent: options.packageManager, command: options.packageManager })
@@ -1154,7 +1337,8 @@ async function runAddToExisting(frameworkOverride, options) {
1154
1337
  ? getPackageManagerCommands({ agent: config.packageManager, command: config.packageManager })
1155
1338
  : resolvePackageManager(cwd);
1156
1339
  const cliExample = pm.dlx('rizzo-css theme');
1157
- writeRizzoConfig(cwd, { targetDir: targetDirRaw, framework, packageManager: pm.agent });
1340
+ const configTargetDir = framework === 'astro' ? 'public/css' : framework === 'svelte' ? 'static/css' : targetDirRaw;
1341
+ writeRizzoConfig(cwd, { targetDir: configTargetDir, framework, packageManager: pm.agent });
1158
1342
  console.log('\n✓ Rizzo CSS added to your existing project');
1159
1343
  console.log(' - ' + cssTarget);
1160
1344
  console.log(' - Wrote ' + RIZZO_CONFIG_FILE);
@@ -1316,6 +1500,13 @@ async function cmdInit(argv) {
1316
1500
  const useVanillaFull = selectedTemplate === 'full' && framework === 'vanilla' && existsSync(getScaffoldVanillaIndex());
1317
1501
  const useVanillaMinimal = selectedTemplate === 'minimal' && framework === 'vanilla';
1318
1502
 
1503
+ // Full and minimal get all required dependencies so everything works; manual gets deps when user picks (see prompt labels).
1504
+ let componentsToCopy = selectedComponents;
1505
+ if ((framework === 'astro' || framework === 'svelte') && selectedComponents.length > 0) {
1506
+ componentsToCopy = expandWithDeps(framework, selectedComponents);
1507
+ logAddedDeps(selectedComponents, componentsToCopy, framework);
1508
+ }
1509
+
1319
1510
  let cssTarget;
1320
1511
  let indexPath;
1321
1512
 
@@ -1343,9 +1534,9 @@ async function cmdInit(argv) {
1343
1534
  console.warn('\nWarning: rizzo.min.css is very small. From repo root run: pnpm build:css');
1344
1535
  }
1345
1536
  copyPackageLicense(projectDir);
1346
- if (selectedComponents.length > 0) {
1537
+ if (componentsToCopy.length > 0) {
1347
1538
  copyRizzoIcons(projectDir, 'astro');
1348
- copyAstroComponents(projectDir, selectedComponents);
1539
+ copyAstroComponents(projectDir, componentsToCopy);
1349
1540
  }
1350
1541
  } else if (useAstroBase) {
1351
1542
  mkdirSync(projectDir, { recursive: true });
@@ -1356,39 +1547,35 @@ async function cmdInit(argv) {
1356
1547
  console.warn('\nWarning: rizzo.min.css is very small. From repo root run: pnpm build:css');
1357
1548
  }
1358
1549
  copyPackageLicense(projectDir);
1359
- if (selectedComponents.length > 0) {
1550
+ if (componentsToCopy.length > 0) {
1360
1551
  copyRizzoIcons(projectDir, 'astro');
1361
- copyAstroComponents(projectDir, selectedComponents);
1552
+ copyAstroComponents(projectDir, componentsToCopy);
1362
1553
  }
1363
1554
  } else if (useHandpickSvelte) {
1364
1555
  mkdirSync(projectDir, { recursive: true });
1365
1556
  copyDirRecursiveWithReplacements(svelteMinimalDir, projectDir, replacements);
1366
- mkdirSync(join(projectDir, 'static', 'css'), { recursive: true });
1557
+ copyRizzoCssAndFontsForSvelte(projectDir, cssSource);
1367
1558
  cssTarget = join(projectDir, 'static', 'css', 'rizzo.min.css');
1368
- copyFileSync(cssSource, cssTarget);
1369
- copyRizzoFonts(dirname(cssTarget));
1370
1559
  if (statSync(cssTarget).size < 5000) {
1371
1560
  console.warn('\nWarning: rizzo.min.css is very small. From repo root run: pnpm build:css');
1372
1561
  }
1373
1562
  copyPackageLicense(projectDir);
1374
- if (selectedComponents.length > 0) {
1563
+ if (componentsToCopy.length > 0) {
1375
1564
  copyRizzoIcons(projectDir, 'svelte');
1376
- copySvelteComponents(projectDir, selectedComponents);
1565
+ copySvelteComponents(projectDir, componentsToCopy);
1377
1566
  }
1378
1567
  } else if (useSvelteBase) {
1379
1568
  mkdirSync(projectDir, { recursive: true });
1380
1569
  copyDirRecursiveWithReplacements(svelteMinimalDir, projectDir, replacements);
1381
- mkdirSync(join(projectDir, 'static', 'css'), { recursive: true });
1570
+ copyRizzoCssAndFontsForSvelte(projectDir, cssSource);
1382
1571
  cssTarget = join(projectDir, 'static', 'css', 'rizzo.min.css');
1383
- copyFileSync(cssSource, cssTarget);
1384
- copyRizzoFonts(dirname(cssTarget));
1385
1572
  if (statSync(cssTarget).size < 5000) {
1386
1573
  console.warn('\nWarning: rizzo.min.css is very small. From repo root run: pnpm build:css');
1387
1574
  }
1388
1575
  copyPackageLicense(projectDir);
1389
- if (selectedComponents.length > 0) {
1576
+ if (componentsToCopy.length > 0) {
1390
1577
  copyRizzoIcons(projectDir, 'svelte');
1391
- copySvelteComponents(projectDir, selectedComponents);
1578
+ copySvelteComponents(projectDir, componentsToCopy);
1392
1579
  }
1393
1580
  } else if (useVanillaFull) {
1394
1581
  const cssDir = join(projectDir, pathsForScaffold.targetDir);
@@ -1449,11 +1636,16 @@ async function cmdInit(argv) {
1449
1636
  writeFileSync(join(projectDir, SCAFFOLD_README_FILENAME), VANILLA_MINIMAL_README, 'utf8');
1450
1637
  copyPackageLicense(projectDir);
1451
1638
  } else {
1452
- const cssDir = join(projectDir, pathsForScaffold.targetDir);
1453
- cssTarget = join(cssDir, 'rizzo.min.css');
1454
- mkdirSync(cssDir, { recursive: true });
1455
- copyFileSync(cssSource, cssTarget);
1456
- copyRizzoFonts(dirname(cssTarget));
1639
+ if (framework === 'svelte') {
1640
+ copyRizzoCssAndFontsForSvelte(projectDir, cssSource);
1641
+ cssTarget = join(projectDir, 'static', 'css', 'rizzo.min.css');
1642
+ } else {
1643
+ const cssDir = join(projectDir, pathsForScaffold.targetDir);
1644
+ cssTarget = join(cssDir, 'rizzo.min.css');
1645
+ mkdirSync(cssDir, { recursive: true });
1646
+ copyFileSync(cssSource, cssTarget);
1647
+ copyRizzoFonts(dirname(cssTarget));
1648
+ }
1457
1649
  if (statSync(cssTarget).size < 5000) {
1458
1650
  console.warn('\nWarning: rizzo.min.css is very small. From repo root run: pnpm build:css');
1459
1651
  }
@@ -1553,6 +1745,10 @@ function main() {
1553
1745
  const command = (argv[0] || 'help').toLowerCase().replace(/^--?/, '');
1554
1746
 
1555
1747
  if (command === 'help' || command === 'h' || !COMMANDS.includes(command)) {
1748
+ if (argv[1] === 'components') {
1749
+ printHelpComponents();
1750
+ return;
1751
+ }
1556
1752
  if (argv[0] && !COMMANDS.includes(command) && command !== 'h') {
1557
1753
  console.error('Unknown command: ' + argv[0] + '\n');
1558
1754
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rizzo-css",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "scripts": {
5
5
  "prepublishOnly": "cd ../.. && pnpm run lint:css:fix && pnpm run build:css && node scripts/copy-scaffold.js && node scripts/prepare-vanilla-scaffold.js"
6
6
  },
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import Cat from './icons/Cat.astro';
3
+ import Gear from './icons/Gear.astro';
3
4
  interface Props { siteName?: string; logo?: string; }
4
5
  const { siteName = 'Site', logo } = Astro.props;
5
6
  ---
@@ -15,6 +16,12 @@ const { siteName = 'Site', logo } = Astro.props;
15
16
  {siteName}
16
17
  </a>
17
18
  </div>
19
+ <div class="navbar__actions-desktop">
20
+ <button type="button" class="navbar__settings-btn" aria-label="Open settings" onclick="window.openSettings && window.openSettings()">
21
+ <Gear class="navbar__settings-icon" width={20} height={20} />
22
+ <span class="navbar__settings-label">Settings</span>
23
+ </button>
24
+ </div>
18
25
  <button type="button" class="navbar__toggle" aria-label="Toggle menu" aria-expanded="false">
19
26
  <span class="navbar__toggle-icon" aria-hidden="true"><span></span><span></span><span></span></span>
20
27
  </button>