rizzo-css 0.0.30 → 0.0.31

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 (33) hide show
  1. package/README.md +1 -1
  2. package/bin/rizzo-css.js +6 -6
  3. package/package.json +1 -1
  4. package/scaffold/astro/Navbar.astro +36 -2
  5. package/scaffold/astro/Search.astro +42 -0
  6. package/scaffold/vanilla/README-RIZZO.md +4 -2
  7. package/scaffold/vanilla/components/accordion.html +8 -0
  8. package/scaffold/vanilla/components/alert.html +8 -0
  9. package/scaffold/vanilla/components/avatar.html +8 -0
  10. package/scaffold/vanilla/components/badge.html +8 -0
  11. package/scaffold/vanilla/components/breadcrumb.html +8 -0
  12. package/scaffold/vanilla/components/button.html +8 -0
  13. package/scaffold/vanilla/components/cards.html +8 -0
  14. package/scaffold/vanilla/components/copy-to-clipboard.html +8 -0
  15. package/scaffold/vanilla/components/divider.html +8 -0
  16. package/scaffold/vanilla/components/dropdown.html +8 -0
  17. package/scaffold/vanilla/components/forms.html +8 -0
  18. package/scaffold/vanilla/components/icons.html +8 -0
  19. package/scaffold/vanilla/components/index.html +8 -0
  20. package/scaffold/vanilla/components/modal.html +8 -0
  21. package/scaffold/vanilla/components/navbar.html +8 -0
  22. package/scaffold/vanilla/components/pagination.html +8 -0
  23. package/scaffold/vanilla/components/progress-bar.html +8 -0
  24. package/scaffold/vanilla/components/search.html +8 -0
  25. package/scaffold/vanilla/components/settings.html +8 -0
  26. package/scaffold/vanilla/components/spinner.html +8 -0
  27. package/scaffold/vanilla/components/table.html +8 -0
  28. package/scaffold/vanilla/components/tabs.html +8 -0
  29. package/scaffold/vanilla/components/theme-switcher.html +8 -0
  30. package/scaffold/vanilla/components/toast.html +8 -0
  31. package/scaffold/vanilla/components/tooltip.html +8 -0
  32. package/scaffold/vanilla/index.html +8 -0
  33. package/scaffold/vanilla/js/main.js +78 -1
package/README.md CHANGED
@@ -61,7 +61,7 @@ import 'rizzo-css';
61
61
  **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:
62
62
 
63
63
  ```html
64
- <!-- unpkg (pin version: replace @latest with @0.0.30 or any version) -->
64
+ <!-- unpkg (pin version: replace @latest with @0.0.31 or any version) -->
65
65
  <link rel="stylesheet" href="https://unpkg.com/rizzo-css@latest/dist/rizzo.min.css" />
66
66
 
67
67
  <!-- or jsDelivr -->
package/bin/rizzo-css.js CHANGED
@@ -58,7 +58,7 @@ Manual setup: HTML + CSS, plus any component pages you chose. Scaffolded with \`
58
58
  - If you picked components, \`components/\` has their HTML pages and \`js/main.js\` is included (open \`components/index.html\` to browse).
59
59
  - Set a theme: \`<html data-theme="github-dark-classic">\` (see \`npx rizzo-css theme\` for all themes).
60
60
 
61
- **If you chose no components:** To add component JavaScript (modal, dropdown, tabs, toast, search, navbar, theme switcher, etc.), use the [Vanilla component docs](https://rizzo-css.vercel.app/docs/vanilla/components) or run \`npx rizzo-css init\` with Vanilla → **Full** in a temp folder and copy \`js/main.js\` and \`icons/\` into this project.
61
+ **If you chose no components:** To add component JavaScript (modal, dropdown, tabs, toast, search, navbar, copy-to-clipboard, theme switcher, etc.), use the [Vanilla component docs](https://rizzo-css.vercel.app/docs/vanilla/components) or run \`npx rizzo-css init\` with Vanilla → **Full** in a temp folder and copy \`js/main.js\` and \`icons/\` into this project.
62
62
 
63
63
  Docs: [rizzo-css.vercel.app](https://rizzo-css.vercel.app)
64
64
  `;
@@ -114,7 +114,7 @@ const RECOMMENDED_COMPONENTS = [
114
114
  'Button', 'Badge', 'Card', 'Modal', 'Tabs', 'ThemeSwitcher', 'FormGroup', 'Alert', 'Toast', 'Dropdown',
115
115
  ];
116
116
 
117
- // Vanilla components that need js/main.js for interactivity (modal, dropdown, tabs, toast, search, navbar mobile, theme switcher).
117
+ // Vanilla components that need js/main.js for interactivity (modal, dropdown, tabs, toast, search, navbar mobile, copy-to-clipboard, theme switcher).
118
118
  const VANILLA_JS_COMPONENTS = ['Modal', 'Dropdown', 'Tabs', 'Toast', 'ThemeSwitcher'];
119
119
 
120
120
  // Component dependencies per framework: when user selects a component, these are copied automatically so it works.
@@ -465,7 +465,7 @@ async function confirmRunInstall(pm) {
465
465
 
466
466
  /** Ask user to copy js/main.js for vanilla interactive components. */
467
467
  async function confirmCopyVanillaJs() {
468
- const answer = await question('\nCopy js/main.js for modal, dropdown, tabs, toast, search, navbar mobile, theme switcher? (Y/n) ');
468
+ const answer = await question('\nCopy js/main.js for modal, dropdown, tabs, toast, search, navbar, copy-to-clipboard, theme switcher? (Y/n) ');
469
469
  return answer === '' || /^y(es)?$/i.test(answer);
470
470
  }
471
471
 
@@ -811,7 +811,7 @@ Options (add):
811
811
  --no-snippet Do not write RIZZO-SNIPPET.txt (link + theme copy-paste)
812
812
  --readme Write README-RIZZO.md into the project
813
813
  --force Overwrite existing rizzo.min.css without prompting
814
- --vanilla-js (Vanilla) Copy js/main.js for interactive components (modal, dropdown, tabs, toast, search, navbar, theme switcher)
814
+ --vanilla-js (Vanilla) Copy js/main.js for interactive components (modal, dropdown, tabs, toast, search, navbar, copy-to-clipboard, theme switcher)
815
815
 
816
816
  Package managers:
817
817
  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.
@@ -1465,7 +1465,7 @@ async function runAddToExisting(frameworkOverride, options) {
1465
1465
  let mainJs = readFileSync(vanillaJsSrc, 'utf8');
1466
1466
  mainJs = mainJs.replace(/\{\{DEFAULT_DARK\}\}/g, defaultDark).replace(/\{\{DEFAULT_LIGHT\}\}/g, defaultLight);
1467
1467
  writeFileSync(vanillaJsPath, mainJs, 'utf8');
1468
- console.log(' - Wrote js/main.js (for modal, dropdown, tabs, toast, search, navbar mobile, theme switcher)');
1468
+ console.log(' - Wrote js/main.js (for modal, dropdown, tabs, toast, search, navbar, copy-to-clipboard, theme switcher)');
1469
1469
  }
1470
1470
  } else if (needsJs && !existsSync(vanillaJsPath)) {
1471
1471
  options._vanillaJsHint = true;
@@ -1526,7 +1526,7 @@ async function runAddToExisting(frameworkOverride, options) {
1526
1526
  console.log(' data-theme="' + theme + '" on <html> (themes: ' + cliExample + ')');
1527
1527
  console.log(' Component HTML files are in components/.');
1528
1528
  if (options._vanillaJsHint) {
1529
- console.log(' For interactive components (modal, dropdown, tabs, toast, search, navbar, theme switcher), add js/main.js — run again with --vanilla-js or copy from a Full scaffold.');
1529
+ console.log(' For interactive components (modal, dropdown, tabs, toast, search, navbar, copy-to-clipboard, theme switcher), add js/main.js — run again with --vanilla-js or copy from a Full scaffold.');
1530
1530
  }
1531
1531
  }
1532
1532
  console.log('\nTo install the package: ' + pm.add('rizzo-css'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rizzo-css",
3
- "version": "0.0.30",
3
+ "version": "0.0.31",
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
  },
@@ -24,11 +24,45 @@ const { siteName = 'Site', logo } = Astro.props;
24
24
  <span class="navbar__settings-label">Settings</span>
25
25
  </button>
26
26
  </div>
27
- <button type="button" class="navbar__toggle" aria-label="Toggle menu" aria-expanded="false">
27
+ <button type="button" class="navbar__toggle" id="navbar-toggle" aria-label="Toggle navigation menu" aria-expanded="false" aria-controls="navbar-menu">
28
+ <span class="sr-only">Menu</span>
28
29
  <span class="navbar__toggle-icon" aria-hidden="true"><span></span><span></span><span></span></span>
29
30
  </button>
30
- <div class="navbar__menu" aria-hidden="true">
31
+ <div class="navbar__menu" id="navbar-menu" role="menu" aria-hidden="true">
31
32
  <a href="/" class="navbar__link">Home</a>
32
33
  </div>
33
34
  </div>
34
35
  </nav>
36
+
37
+ <script>
38
+ (function initNavbarMobile() {
39
+ function init() {
40
+ var navbar = document.querySelector('.navbar');
41
+ if (!navbar) return;
42
+ var toggle = document.getElementById('navbar-toggle');
43
+ var menu = navbar.querySelector('.navbar__menu');
44
+ if (!toggle || !menu) return;
45
+ function setMenuOpen(open) {
46
+ menu.classList.toggle('navbar__menu--open', open);
47
+ navbar.classList.toggle('navbar--menu-open', open);
48
+ toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
49
+ menu.setAttribute('aria-hidden', open ? 'false' : 'true');
50
+ }
51
+ toggle.addEventListener('click', function () {
52
+ setMenuOpen(!menu.classList.contains('navbar__menu--open'));
53
+ });
54
+ menu.querySelectorAll('.navbar__link').forEach(function (link) {
55
+ link.addEventListener('click', function () { setMenuOpen(false); });
56
+ });
57
+ document.addEventListener('keydown', function (e) {
58
+ if (e.key === 'Escape') {
59
+ if (menu.classList.contains('navbar__menu--open')) {
60
+ setMenuOpen(false);
61
+ }
62
+ }
63
+ });
64
+ }
65
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
66
+ else init();
67
+ })();
68
+ </script>
@@ -1,10 +1,12 @@
1
1
  ---
2
+ import SearchIcon from './icons/Search.astro';
2
3
  interface Props { id?: string; }
3
4
  const { id = 'search-main' } = Astro.props;
4
5
  ---
5
6
  <div class="search" data-search>
6
7
  <div class="search__trigger-wrapper">
7
8
  <button type="button" class="search__trigger" aria-label="Open search" aria-expanded="false" aria-controls="{id}-panel">
9
+ <SearchIcon width={20} height={20} class="search__icon" />
8
10
  <span class="search__trigger-text">Search</span>
9
11
  </button>
10
12
  </div>
@@ -14,3 +16,43 @@ const { id = 'search-main' } = Astro.props;
14
16
  </div>
15
17
  </div>
16
18
  </div>
19
+
20
+ <script>
21
+ (function initSearch() {
22
+ function init() {
23
+ document.querySelectorAll('[data-search]').forEach(function (search) {
24
+ if (search.__searchInited) return;
25
+ search.__searchInited = true;
26
+ var trigger = search.querySelector('.search__trigger');
27
+ var overlay = search.querySelector('[data-search-overlay]');
28
+ var input = search.querySelector('.search__input');
29
+ if (!trigger || !overlay || !input) return;
30
+ var previousActive = null;
31
+ function openSearch() {
32
+ previousActive = document.activeElement;
33
+ overlay.setAttribute('aria-hidden', 'false');
34
+ trigger.setAttribute('aria-expanded', 'true');
35
+ input.focus();
36
+ }
37
+ function closeSearch() {
38
+ overlay.setAttribute('aria-hidden', 'true');
39
+ trigger.setAttribute('aria-expanded', 'false');
40
+ if (previousActive && previousActive.focus) previousActive.focus();
41
+ previousActive = null;
42
+ }
43
+ trigger.addEventListener('click', function () {
44
+ if (overlay.getAttribute('aria-hidden') === 'true') openSearch();
45
+ else closeSearch();
46
+ });
47
+ overlay.addEventListener('click', function (e) {
48
+ if (e.target === overlay) closeSearch();
49
+ });
50
+ input.addEventListener('keydown', function (e) {
51
+ if (e.key === 'Escape') { e.preventDefault(); closeSearch(); }
52
+ });
53
+ });
54
+ }
55
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
56
+ else init();
57
+ })();
58
+ </script>
@@ -13,7 +13,7 @@ If you prefer to load CSS from a CDN instead of the local file, replace the `<li
13
13
  - `<link rel="stylesheet" href="https://unpkg.com/rizzo-css@latest/dist/rizzo.min.css" />`
14
14
  - Or jsDelivr: `https://cdn.jsdelivr.net/npm/rizzo-css@latest/dist/rizzo.min.css`
15
15
 
16
- (Replace `@latest` with a specific version, e.g. `@0.0.30`, in production.)
16
+ (Replace `@latest` with a specific version, e.g. `@0.0.31`, in production.)
17
17
 
18
18
  The CLI replaces placeholders in `index.html` (e.g. `{{DATA_THEME}}`, `{{TITLE}}`) when you run `rizzo-css init`. The theme selected during init is used on first load when you have no saved preference in the browser.
19
19
 
@@ -22,7 +22,7 @@ The CLI replaces placeholders in `index.html` (e.g. `{{DATA_THEME}}`, `{{TITLE}}
22
22
  - **Home** — `index.html` (hero, links to component showcase and docs). Edit the main content or add your own.
23
23
  - **Component showcase** — `components/index.html` lists all components; `components/<name>.html` (e.g. `button.html`) each has a "Read the full docs" link to the main site. Edit or add HTML files; keep the same header/footer if you want the theme switcher and settings on every page.
24
24
  - **CSS** — The CLI copies `css/rizzo.min.css`; the link uses `{{LINK_HREF}}` (replaced at init). To use a CDN, replace that with the CDN URL.
25
- - **Scripts** — `js/main.js` provides theme sync, settings panel, toast, tabs, modal, dropdown, accordion, search (overlay), and navbar mobile menu. Customize or extend as needed.
25
+ - **Scripts** — `js/main.js` provides theme sync, settings panel, toast, tabs, modal, dropdown, accordion, search (overlay), navbar mobile menu, and copy-to-clipboard. Customize or extend as needed.
26
26
 
27
27
  ## What's included
28
28
 
@@ -37,6 +37,8 @@ The CLI replaces placeholders in `index.html` (e.g. `{{DATA_THEME}}`, `{{TITLE}}
37
37
  - **Accordion** — Any `[data-accordion]` with `[data-accordion-trigger]` and panels; `data-allow-multiple="true"` for multiple open.
38
38
  - **Search** — Any `[data-search]` with `.search__trigger`, `[data-search-overlay]`, `.search__panel`, and `.search__input`; trigger toggles overlay, Escape or overlay click closes.
39
39
  - **Navbar** — Mobile menu: `.navbar__toggle` toggles `.navbar__menu`; Escape closes.
40
+ - **Copy to clipboard** — Buttons with `.copy-to-clipboard` and `data-copy-value`, or `[data-copy]` with `value` or `data-copy-value`; click copies text and shows feedback (icons/aria-label). Optional `data-copy-format` for “Copied {format}!”.
41
+ - **Tooltips** — Use `.tooltip-wrapper` with a `.tooltip` child, or `[data-tooltip]` on the trigger; no JS required (CSS :hover and :focus-within).
40
42
 
41
43
  ## Commands
42
44
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Rizzo CSS — Vanilla JS component bundle
3
- * Theme, toast, settings, tabs, modal, dropdown, accordion, search, navbar (mobile).
3
+ * Theme, toast, settings, tabs, modal, dropdown, accordion, search, navbar (mobile), copy-to-clipboard.
4
4
  * Load this script after the DOM (e.g. before </body>).
5
5
  */
6
6
  (function () {
@@ -284,6 +284,82 @@
284
284
  window.openSettings = openSettings;
285
285
  }
286
286
 
287
+ // --- Copy to clipboard: .copy-to-clipboard [data-copy-value] or [data-copy] with value ---
288
+ function initCopyToClipboard() {
289
+ function setupButton(btn) {
290
+ if (btn.getAttribute('data-copy-inited') === 'true') return;
291
+ btn.setAttribute('data-copy-inited', 'true');
292
+ var host = btn.closest('.tooltip-host');
293
+ var defaultTooltip = (host && host.getAttribute('data-tooltip')) || 'Copy to clipboard';
294
+ if (host) host.setAttribute('data-copy-default-tooltip', defaultTooltip);
295
+ var defaultAria = btn.getAttribute('aria-label') || 'Copy to clipboard';
296
+ var getValue = function () { return btn.getAttribute('data-copy-value') || btn.getAttribute('value') || ''; };
297
+ var getFormat = function () { return btn.getAttribute('data-copy-format') || ''; };
298
+ var copyIcon = btn.querySelector('.copy-to-clipboard__icon--copy');
299
+ var checkIcon = btn.querySelector('.copy-to-clipboard__icon--check');
300
+ var feedback = btn.querySelector('.copy-to-clipboard__feedback');
301
+ var textSpan = btn.querySelector('.copy-to-clipboard__text');
302
+ function doCopy() {
303
+ var value = getValue();
304
+ if (!value && textSpan) value = textSpan.textContent || '';
305
+ if (!value) return;
306
+ function showSuccess() {
307
+ if (copyIcon) copyIcon.classList.add('copy-to-clipboard__icon--hidden');
308
+ if (checkIcon) checkIcon.classList.remove('copy-to-clipboard__icon--hidden');
309
+ if (feedback) feedback.textContent = getFormat() ? 'Copied ' + getFormat() + '!' : 'Copied!';
310
+ if (host) host.setAttribute('data-tooltip', getFormat() ? 'Copied ' + getFormat() + '!' : 'Copied!');
311
+ btn.setAttribute('aria-label', getFormat() ? 'Copied ' + getFormat() + '!' : 'Copied!');
312
+ var labelEl = btn.querySelector('.copy-trigger__text');
313
+ var previousLabel = labelEl ? labelEl.textContent : '';
314
+ if (labelEl) labelEl.textContent = 'Copied!';
315
+ setTimeout(function () {
316
+ if (copyIcon) copyIcon.classList.remove('copy-to-clipboard__icon--hidden');
317
+ if (checkIcon) checkIcon.classList.add('copy-to-clipboard__icon--hidden');
318
+ if (feedback) feedback.textContent = '';
319
+ if (host) host.setAttribute('data-tooltip', host.getAttribute('data-copy-default-tooltip') || defaultTooltip);
320
+ btn.setAttribute('aria-label', defaultAria);
321
+ if (labelEl) labelEl.textContent = previousLabel || 'Copy';
322
+ }, 2000);
323
+ }
324
+ if (typeof navigator.clipboard !== 'undefined' && navigator.clipboard.writeText) {
325
+ navigator.clipboard.writeText(value).then(showSuccess).catch(function () {
326
+ try {
327
+ var ta = document.createElement('textarea');
328
+ ta.value = value;
329
+ ta.style.position = 'fixed';
330
+ ta.style.left = '-9999px';
331
+ document.body.appendChild(ta);
332
+ ta.focus();
333
+ ta.select();
334
+ document.execCommand('copy');
335
+ document.body.removeChild(ta);
336
+ showSuccess();
337
+ } catch (e) {
338
+ if (window.showToast) window.showToast('Failed to copy', { variant: 'warning' });
339
+ }
340
+ });
341
+ } else {
342
+ try {
343
+ var ta = document.createElement('textarea');
344
+ ta.value = value;
345
+ ta.style.position = 'fixed';
346
+ ta.style.left = '-9999px';
347
+ document.body.appendChild(ta);
348
+ ta.focus();
349
+ ta.select();
350
+ document.execCommand('copy');
351
+ document.body.removeChild(ta);
352
+ showSuccess();
353
+ } catch (e) {
354
+ if (window.showToast) window.showToast('Failed to copy', { variant: 'warning' });
355
+ }
356
+ }
357
+ }
358
+ btn.addEventListener('click', doCopy);
359
+ }
360
+ document.querySelectorAll('.copy-to-clipboard[data-copy-value], .copy-to-clipboard[data-copy], [data-copy]').forEach(setupButton);
361
+ }
362
+
287
363
  // --- Tabs: init all [data-tabs] ---
288
364
  function initTabs() {
289
365
  document.querySelectorAll('[data-tabs]').forEach(function (tabsContainer) {
@@ -801,6 +877,7 @@
801
877
  function run() {
802
878
  initTheme();
803
879
  initSettings();
880
+ initCopyToClipboard();
804
881
  initTabs();
805
882
  initModals();
806
883
  initDropdowns();