{"_id":"@digi-archive/hotwire-astra-ui","name":"@digi-archive/hotwire-astra-ui","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@digi-archive/hotwire-astra-ui","version":"0.1.0","description":"Accessible Hotwire modal component for Rails (Turbo + Stimulus) with Importmap support","license":"MIT","type":"module","sideEffects":["*.css"],"exports":{".":"./app/javascript/hotwire-astra-ui.js","./styles.css":"./hotwire-astra-ui.css"},"_id":"@digi-archive/hotwire-astra-ui@0.1.0","_nodeVersion":"20.18.2","_npmVersion":"11.1.0","dist":{"integrity":"sha512-y1HzyqcS7YiEXaWNsdLpSxXUQ9PVBc3h5DMJibyuT7y1NZ76k7Q3kpAux8MVZsXE/l8MqEkWOv0ajG2Umooyow==","shasum":"e2773d9d6b60b51ffabfbf677aaffe9d525ac3ec","tarball":"https://registry.npmjs.org/@digi-archive/hotwire-astra-ui/-/hotwire-astra-ui-0.1.0.tgz","fileCount":5,"unpackedSize":13705,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDPgbFcDP/1GzANvDvJvBDEXuMqcysm4ki2p6zMZyWhZAIgfCRRFR4hS1hk73n5kkgXZzCwp+FGYLpjf3E54RrhvAc="}]},"_npmUser":{"name":"wwwfernand","email":"dev.digi.archive@gmail.com"},"directories":{},"maintainers":[{"name":"wwwfernand","email":"dev.digi.archive@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/hotwire-astra-ui_0.1.0_1770207534572_0.6545116186891287"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-04T12:18:54.402Z","0.1.0":"2026-02-04T12:18:54.717Z","modified":"2026-02-04T12:18:55.048Z"},"maintainers":[{"name":"wwwfernand","email":"dev.digi.archive@gmail.com"}],"description":"Accessible Hotwire modal component for Rails (Turbo + Stimulus) with Importmap support","license":"MIT","readme":"# Hotwire Astra UI 🌌\n\n**Hotwire Astra UI** is a **production-ready, accessible modal system for Rails** built with **Hotwire (Turbo + Stimulus)** and **Importmap**.\n\nIt enables **fully server-rendered modals** using Turbo Frames, with **zero client-side configuration** and **no JavaScript frameworks**.\n\nIf you are building Rails 7+ applications and want predictable, scalable modal behavior without SPA complexity, Astra UI is designed for you.\n\n---\n\n## ✨ Features\n\n### 🔌 Zero-JavaScript Setup\nDesigned for **Rails 7+**, **Hotwire**, and **Importmap**.\nInstall the gem, run the generator, and start rendering modals immediately.\n\nNo client state. No bundlers. No framework lock-in.\n\n---\n\n### 🪜 Recursive (Stacked) Modals\nOpen modals **on top of modals** using nested Turbo Frames.\n\nEach modal layer:\n- is independently closable\n- maintains its own lifecycle\n- works with normal Rails controllers\n\nInfinite stacking, zero configuration.\n\n---\n\n### 🎞️ CSS-Driven Animations\nEntry and exit transitions are powered by **CSS variables**, not JavaScript.\n\n- Works with Tailwind or plain CSS\n- Respects `prefers-reduced-motion`\n- Fully overrideable\n\n---\n\n### 🔄 Automatic Close on Turbo Success\nModals close automatically after successful Turbo form submissions.\n\nNo callbacks. No client logic.\nJust follow the Turbo lifecycle.\n\n---\n\n### 🎨 Headless UI Design\nAstra UI ships with safe defaults, but **nothing is opinionated**.\n\n- Override everything with CSS variables\n- Compatible with design systems\n- No forced colors, spacing, or typography\n\n---\n\n### ♿ Accessibility-First\n- Proper dialog semantics\n- Keyboard navigation\n- Focus management\n- ESC and backdrop handling\n\n---\n\n## 📦 Installation\n\nAdd the gem to your `Gemfile`:\n\n```ruby\ngem \"hotwire-astra-ui\", \"0.1.0\"\n```\n\nInstall and run the generator:\n\n```bash\nbundle install\nbin/rails generate hotwire_astra_ui:install\n```\n\nThe installer registers the Stimulus controller for you. If your app does not have `app/javascript/controllers/index.js`, add this registration manually:\n\n```js\nimport HotwireAstraUiModalController from \"hotwire_astra_ui/modal_controller\"\napplication.register(\"hotwire-astra-ui-modal\", HotwireAstraUiModalController)\n```\n\nThe installer adds a persistent Turbo Frame to your layout:\n\n```erb\n<%= turbo_frame_tag \"astra_modal\" %>\n```\n\nThis frame is used to render modal content.\n\n## 🧩 JS-Only (Importmap Pin)\n\nIf you only want the **Stimulus controller** (and will provide your own HTML/CSS), you can pin the JS directly with Importmap:\n\n```ruby\n# config/importmap.rb\npin \"hotwire_astra_ui/modal_controller\", to: \"\"\n```\n\nRegister the controller:\n\n```js\nimport HotwireAstraUiModalController from \"hotwire_astra_ui/modal_controller\"\napplication.register(\"hotwire-astra-ui-modal\", HotwireAstraUiModalController)\n```\n\nYou must also provide:\n\n- The modal HTML structure with the correct `data-controller` and target attributes\n- The CSS (either copy `app/assets/stylesheets/hotwire/astra_ui/astra_ui.css` or reimplement it)\n\nExample HTML structure (JS-only):\n\n```erb\n<div data-controller=\"hotwire-astra-ui-modal\"\n     data-hotwire-astra-ui-modal-target=\"backdrop\"\n     data-action=\"click->hotwire-astra-ui-modal#backdropClick\"\n     class=\"astra-modal-backdrop astra-default\">\n\n  <div class=\"astra-modal-container astra-modal-size-md\"\n       data-hotwire-astra-ui-modal-target=\"container\"\n       data-action=\"click->hotwire-astra-ui-modal#stopPropagation\"\n       role=\"dialog\"\n       aria-modal=\"true\"\n       aria-label=\"Example Dialog\"\n       tabindex=\"-1\">\n    <div class=\"astra-modal-header\">\n      <h2>Example Dialog</h2>\n      <button type=\"button\" data-action=\"hotwire-astra-ui-modal#close\" class=\"astra-close-btn\" aria-label=\"Close dialog\">&times;</button>\n    </div>\n\n    <div class=\"astra-modal-body\">\n      Your content goes here.\n    </div>\n  </div>\n</div>\n```\n\n### 📦 npm + Importmap (JS + CSS)\n\nIf you publish the npm package `@digi-archive/hotwire-astra-ui`, you can pin it via Importmap using the jspm CDN:\n\n```ruby\n# config/importmap.rb\npin \"@digi-archive/hotwire-astra-ui\", to: \"https://ga.jspm.io/npm:@digi-archive/hotwire-astra-ui@0.1.0/app/javascript/hotwire-astra-ui.js\"\n```\n\nRegister the controller:\n\n```js\nimport HotwireAstraUiModalController from \"@digi-archive/hotwire-astra-ui\"\napplication.register(\"hotwire-astra-ui-modal\", HotwireAstraUiModalController)\n```\n\nTo load the CSS from npm, include the stylesheet from the same CDN:\n\n```erb\n<link rel=\"stylesheet\" href=\"https://ga.jspm.io/npm:@digi-archive/hotwire-astra-ui@0.1.0/hotwire-astra-ui.css\">\n```\n\n## 🎨 Styles\n\nImport the base stylesheet:\n\n```css\n/*\n *= require hotwire_astra_ui/astra_ui\n */\n```\n\nAll visual customization is done via CSS variables.\n\n## 🚀 Basic Usage\n\n1. Trigger the Modal\n\nTarget the modal Turbo Frame:\n\n```erb\n<%= link_to \"New Post\", new_post_path, data: { turbo_frame: \"astra_modal\" } %>\n```\n\n2. Render the Modal in a View Template\n```ruby\n<%= astra_modal(title: \"Create a New Post\") do %>\n  <%= render partial: \"form\" %>\n<% end %>\n```\n\nThat’s it.\nNo JavaScript. No client state.\n\n## 🧩 Advanced Usage\n\n### 🧰 Component Parameters\n\n`Hotwire::AstraUi::ModalComponent.new` accepts:\n\n- `title:` string or nil. If present, renders the header and provides `aria-labelledby`.\n- `id:` Turbo Frame id (default: `\"astra_modal\"`).\n- `theme_class:` CSS class applied to the backdrop (default: `\"astra-default\"`).\n- `aria_label:` used when `title` is nil (default: `\"Dialog\"`).\n- `size:` one of `:sm`, `:md`, `:lg`, `:xl` (default: `:md`).\n- `backdrop_close:` boolean for backdrop click to close (default: `true`).\n- `return_focus:` CSS selector to focus after close (default: `nil`).\n\n### 🪄 Convenience Helper\n\nRender from a view:\n\n```erb\n<%= astra_modal(title: \"Create a New Post\") do %>\n  <%= render partial: \"form\" %>\n<% end %>\n```\n\n### 🔁 Stacked (Nested) Modals\n\nOpen a modal from inside another modal by targeting the next frame:\n\n```erb\n<%= link_to \"Confirm Delete\",\n            confirm_delete_path,\n            data: { turbo_frame: \"astra_modal_next\" } %>\n```\n\nTemplate:\n\n```erb\n<%= astra_modal(title: \"Are you sure?\", id: \"astra_modal_next\") do %>\n  This action cannot be undone.\n<% end %>\n```\n\nEach modal layer remains isolated and predictable.\n\n### 🔒 Close Modals from the Server (Turbo Streams)\n\nClose the modal directly from the server:\n\n```erb\n<%= close_astra_modal_tag %>\n```\n\nClose a specific frame by id:\n\n```erb\n<%= close_astra_modal_tag(id: \"astra_modal_next\") %>\n```\n\nUseful for:\n\n- form submissions\n- background jobs\n- multi-step workflows\n\n### 🧱 Size Variants\n\nSet a modal size with `size:`:\n\n```ruby\nrender Hotwire::AstraUi::ModalComponent.new(title: \"Large Modal\", size: :lg)\n```\n\nAvailable sizes: `:sm`, `:md`, `:lg`, `:xl`.\n\n### 🧲 Backdrop Click & Focus Return\n\nDisable backdrop click to close:\n\n```ruby\nrender Hotwire::AstraUi::ModalComponent.new(title: \"Locked\", backdrop_close: false)\n```\n\nReturn focus to a specific element after close:\n\n```ruby\nrender Hotwire::AstraUi::ModalComponent.new(title: \"Edit\", return_focus: \"#edit-button\")\n```\n\n## 🎨 Customization (CSS Variables)\n\nOverride the look without touching gem code:\n\n```css\n:root {\n  --astra-modal-bg: #1e293b;\n  --astra-modal-radius: 0px;\n  --astra-backdrop-blur: 10px;\n  --astra-transition-duration: 500ms;\n}\n```\n\nWorks with:\n\n- Tailwind CSS\n- Dark mode\n- Design tokens\n- Enterprise UI standards\n\n## 🧪 Development\n\nRun the dummy app locally:\n\n```bash\ncd test/dummy\nbin/rails s\n```\n\n## ✅ Testing\n\nRun the test suite:\n\n```bash\nbin/test\n```\n\nOr directly via Rake:\n\n```bash\nbundle exec rake test\n```\n\n\n## License\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","readmeFilename":"README.md","_rev":"1-b54f632ea79dd1f6b61f09fe8eec10f6"}