baldur 0.1.7 → 0.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0dd4cd67a133fc0b38f6f20c08353714a707643cac229c33d1684092d7049ea9
4
- data.tar.gz: 5ca96debff6a918d861b49869d80de403a70559b3f0858a47eb99bdecf66f91e
3
+ metadata.gz: 633d727781f1701dcac870818b72d1c8df5ea9392cee702502ce5bd45aa1ef8a
4
+ data.tar.gz: aa155789ea73b321dff4cb7d795e5291c7613cbcff8b24142558c8b9cd0e3700
5
5
  SHA512:
6
- metadata.gz: f2d36011fdd64faddafa75c73f9fbcb4d6da1256b65cff928c177c412d895d1f27a3ed2163231435d20529e2fa1f0a4a9802b385a0a9d05c4af20a2bfb2ff4f7
7
- data.tar.gz: c854ed1bdcdae904302b3c58aede5444fd34c5025e3224b89166ff16d2aab4509900e80f499d359680f9028cb51a90511af74938ecdd0632479ec19ac285b69a
6
+ metadata.gz: 00f499098c0a6cd6bd35aa0819e9978e63648f23d3c7e1b1e57f90c3b6775338beb54fbf956e3e1185666b89362e523e39fadb9403c7722b1d97c4353530a568
7
+ data.tar.gz: e45efa594d08c3271fa53a2b56319d92e97e270c2610595c5ff01ce16b9a8d3e7a6b4ece4da564d82ccb8b5e42f80c0160611dc2ae92a51c7e855be9c94a4113
data/README.md CHANGED
@@ -1,24 +1,29 @@
1
1
  # Baldur
2
2
 
3
- Baldur is a reusable Rails UI engine for apps using the same frontend stack as this repository:
3
+ [![Gem Version](https://badge.fury.io/rb/baldur.svg)](https://badge.fury.io/rb/baldur) [![CI](https://github.com/varunmurkar/baldur/actions/workflows/ci.yml/badge.svg)](https://github.com/varunmurkar/baldur/actions)
4
4
 
5
- - Rails 8
5
+ Batteries-included opinionated Rails UI engine for the importmap, Stimulus, Tailwind stack. Baldur helps Rails teams ship polished UI faster with install generators, reusable ui_* helpers, Tailwind components, and Stimulus wiring for apps using Propshaft, importmap-rails, stimulus-rails, and tailwindcss-rails.
6
+
7
+ ## Supported Stack
8
+
9
+ - **Ruby** `>= 3.3`
10
+ - **Rails** `>= 7.0`
6
11
  - Propshaft
7
12
  - `importmap-rails`
8
13
  - `stimulus-rails`
9
- - `tailwindcss-rails`
14
+ - `tailwindcss-rails` `>= 4.3.0`
10
15
 
11
- ## Install Into Another App
16
+ It ships install generators, ready `ui_*` helpers, Tailwind components, and Stimulus controllers so host teams can skip rebuilding common UI primitives per app.
12
17
 
13
- 1. Add Baldur to the target app `Gemfile`:
18
+ ## Install
14
19
 
15
- Baldur now declares `tailwindcss-rails >= 4.4.0` as a gem dependency, so hosts do not need to add that gem separately unless they want to enforce their own minimum version.
20
+ Add to your `Gemfile`:
16
21
 
17
22
  ```ruby
18
23
  gem "baldur", ">= 0.1.3"
19
24
  ```
20
25
 
21
- 2. Run:
26
+ Run:
22
27
 
23
28
  ```sh
24
29
  bundle install
@@ -26,93 +31,24 @@ bundle exec rails tailwindcss:engines
26
31
  bundle exec rails generate baldur:install
27
32
  ```
28
33
 
29
- 3. Rebuild Tailwind:
34
+ Rebuild Tailwind:
30
35
 
31
36
  ```sh
32
37
  bundle exec rails tailwindcss:build
33
38
  ```
34
39
 
35
- `tailwindcss:engines` creates `app/assets/builds/tailwind/baldur.css` from the engine-owned Tailwind entrypoint. `baldur:install` then imports that generated build into the host Tailwind entrypoint.
36
-
37
- If the host app already runs `tailwindcss:build` or `tailwindcss:watch`, those commands will also create the engine build automatically once the engine entrypoint exists.
38
-
39
- 4. Install optional surfaces as needed:
40
+ Optional surfaces:
40
41
 
41
42
  ```sh
42
43
  bundle exec rails generate baldur:install_panel_secondary
43
44
  bundle exec rails generate baldur:install_google_auth
44
45
  ```
45
46
 
46
- `baldur:install` now includes `Baldur::Optional::AuthPageHelper` in generated `UiHelper`, so `ui_auth_page` is available by default after base install.
47
-
48
- The generators above are still optional and only required when using those specific surfaces.
49
-
50
- Default install behavior keeps Geist loaded through the host `fonts.css` scaffold. If a host app wants a different stack, it should update `fonts.css` and then map the loaded families in `theme.css`.
51
-
52
- ## Security
53
-
54
- - New Baldur releases require MFA for RubyGems owners via gem metadata starting with `0.1.2`.
55
- - Release artifacts should be installed from RubyGems or GitHub releases and can be verified with the published `.sha512` checksum file.
56
- - Report vulnerabilities privately through GitHub Security Advisories.
57
-
58
- To verify a release artifact manually:
59
-
60
- ```sh
61
- sha512sum -c baldur-0.1.3.gem.sha512
62
- ```
63
-
64
- ## Styling Ownership
47
+ ## Example
65
48
 
66
- Tailwind provides the utility/base layer. Baldur is the source of truth for shared design-system primitives.
67
-
68
- - Import host `fonts.css` before Tailwind so the host controls what font families get loaded.
69
- - Import the generated Baldur Tailwind engine build into the host Tailwind entrypoint.
70
- - Import host `theme.css` after the Baldur build to override only the base palette inputs and font-token mapping.
71
- - Treat host `fonts.css` as the project-specific font loading layer.
72
- - Treat host `theme.css` as the project-specific override layer for palette, font-token mapping, and other brand inputs, not as a place to fork Baldur-owned primitive styles.
73
- - Add host-app styles only for app-specific surfaces after Baldur.
74
- - Do not re-import or override host-local copies of Baldur-owned primitives such as buttons, forms, snackbars, or tables.
75
- - Keep shared elevation semantics in Baldur-owned `--elev-*` tokens. If a host app needs softer or stronger shared shadows, change the Baldur token source instead of swapping raw Tailwind shadow utilities into Baldur-owned primitives.
76
- - Do not keep duplicate copies of Baldur-owned primitives under `app/assets/stylesheets/application/`; leave only app-specific files there.
77
- - Do not keep host copies of Baldur semantic theme files such as `theme/light.css` or `theme/dark.css`.
78
-
79
- ## What The Installer Assumes
80
-
81
- - Tailwind entrypoint exists at `app/assets/tailwind/application.css`
82
- - Host app gets `tailwindcss-rails` through Baldur or its own Gemfile and uses engine builds
83
- - Host app uses importmap Stimulus boot with `app/javascript/controllers`
84
- - Host app gets `app/assets/stylesheets/fonts.css` for font loading and `app/assets/stylesheets/theme.css` for brand and font-token overrides
85
- - Host app can import `app/assets/builds/tailwind/baldur.css` from `app/assets/tailwind/application.css`
86
-
87
- ## Building UI
88
-
89
- Canonical Ruby internals live under `Baldur::*`, but the default DX is `ui_*` helpers through the generated `UiHelper` include.
90
-
91
- Not every `ui_*` helper is part of the base install. Some helpers belong to optional surfaces and require the matching generator. `ui_auth_page` is part of the default install; `ui_panel_secondary` and google-auth helpers are optional.
92
-
93
- For authenticated application shells, use `ui_sidebar`. It is part of the base install and includes:
94
-
95
- - desktop sidebar shell markup
96
- - mobile sidebar shell markup
97
- - `sidebar` and `mobile-sidebar` controller wiring
98
- - default brand rendering from `config.marketing_brand`
99
- - default nav rendering for primary and secondary links
100
-
101
- Host apps provide the route arrays, active-state logic, and any optional slot content.
102
-
103
- Examples:
49
+ Render a sidebar with navigation and a main content area:
104
50
 
105
51
  ```erb
106
- <%= ui_button(label: "Save", href: "#") %>
107
-
108
- <%= ui_card(title: "Revenue") do %>
109
- <p>Content</p>
110
- <% end %>
111
-
112
- <%= ui_panel_secondary(id: "assistant", title: "Assistant", trigger_label: "Open") do %>
113
- <p>Panel content</p>
114
- <% end %>
115
-
116
52
  <%= ui_sidebar(
117
53
  brand_path: root_path,
118
54
  primary_links: [
@@ -125,321 +61,87 @@ Examples:
125
61
  <% end %>
126
62
  ```
127
63
 
128
- `ui_sidebar` accepts these link keys:
129
-
130
- - `name` required label text
131
- - `path` required destination
132
- - `icon` optional Lucide icon name, defaults to `circle`
133
- - `active` optional boolean for `aria-current="page"`
134
- - `title` optional hover title
135
- - `method` optional Rails link method
136
- - `data` optional data attributes hash
137
- - `html_options` optional extra HTML attributes hash
138
-
139
- Default brand behavior:
140
-
141
- - `brand_name`, `brand_wordmark`, and `brand_logo` are optional
142
- - when omitted, Baldur resolves them from `config.marketing_brand`
143
- - `brand_path` defaults to `#` if the host app does not provide one
144
-
145
- Mobile behavior:
146
-
147
- - mobile nav mirrors desktop primary and secondary links automatically
148
- - the install generator now ships both `sidebar_controller.js` and `mobile_sidebar_controller.js`
149
-
150
- You can adopt `ui_sidebar` with no slots. The block content becomes the main app surface beside the sidebar:
151
-
152
- ```erb
153
- <%= ui_sidebar(
154
- brand_path: root_path,
155
- primary_links: sidebar_primary_links,
156
- secondary_links: sidebar_secondary_links,
157
- secondary_label: "Admin"
158
- ) do |_sidebar| %>
159
- <main id="main-content" class="flex-1 p-6">
160
- <%= yield %>
161
- </main>
162
- <% end %>
163
- ```
164
-
165
- For host-specific sidebar surfaces, prefer slots. `*_content` params are also supported for incremental adoption.
166
-
167
- ```erb
168
- <%= ui_sidebar(
169
- brand_path: root_path,
170
- primary_links: sidebar_primary_links,
171
- secondary_links: sidebar_secondary_links,
172
- secondary_label: "Admin"
173
- ) do |sidebar| %>
174
- <% sidebar.with_header do %>
175
- <%= render "layouts/tenant_switcher" %>
176
- <% end %>
177
-
178
- <% sidebar.with_footer do %>
179
- <div class="space-y-2">
180
- <p class="text-sm text-muted"><%= current_user.email %></p>
181
- <%= ui_button(label: "Sign out", href: logout_path, method: :delete, variant: :text, size: :sm, icon: "log-out") %>
182
- </div>
183
- <% end %>
184
- <% end %>
185
- ```
186
-
187
- Host apps own:
188
-
189
- - route definitions
190
- - active-state logic
191
- - app-specific header/footer content
192
-
193
- Baldur owns:
194
-
195
- - desktop and mobile sidebar shell markup
196
- - toggle behavior wiring
197
- - default brand rendering
198
- - default nav rendering
199
-
200
- External triggers can open a Baldur panel declaratively:
201
-
202
- ```erb
203
- <button
204
- type="button"
205
- data-open-panel="#assistant"
206
- data-panel-payload="<%= json_escape({ source: "dashboard" }.to_json) %>">
207
- Open assistant
208
- </button>
209
- ```
210
-
211
- `panel-secondary` emits `baldur:panel:opened` and `baldur:panel:closed` on the panel shell and `window`. The event detail includes `id`, `selector`, `trigger`, and parsed `payload`.
212
-
213
- For modals, prefer `ui_modal` directly:
214
-
215
- ```erb
216
- <%= ui_modal(id: "confirm-delete", title: "Delete item") do %>
217
- <p>This action cannot be undone.</p>
218
- <% end %>
219
- ```
220
-
221
- For auth layouts, use `ui_auth_page` (available after `baldur:install`):
222
-
223
- ```erb
224
- <%= ui_auth_page(title: "Sign in", description: nil, brand_path: root_path) do %>
225
- <%= yield %>
226
- <% end %>
227
- ```
228
-
229
- Auth flash messaging can be rendered inside the card by passing `notice:` / `alert:`:
230
-
231
- ```erb
232
- <%= ui_auth_page(title: "Sign in", description: nil, brand_path: root_path, notice: notice, alert: alert) do %>
233
- <%= yield %>
234
- <% end %>
235
- ```
236
-
237
- If a host app keeps a shared wrapper partial around `ui_modal`, treat `modal_body:` as the wrapper-local input and let the wrapper pass that content into `ui_modal`. Avoid calling a wrapper with `body:` through `render`, since `body` collides with Rails render options.
238
-
239
- For horizontal primary/secondary CTA groups, prefer `ui_action_row`:
240
-
241
- ```erb
242
- <%= ui_action_row(
243
- secondary_button: { label: "Back", variant: :outline, href: settings_path },
244
- primary_button: { label: "Save", variant: :primary, type: :submit }
245
- ) %>
246
- ```
247
-
248
- The action row owns the responsive layout and keeps the primary CTA last on the right.
249
-
250
- Use `ui_alert` for inline status surfaces. Alerts support optional inline actions and opt-in collapsed state:
251
-
252
- ```erb
253
- <%= ui_alert(
254
- variant: :warning,
255
- title: "Data freshness warning",
256
- actions: ui_button(label: "Upload Latest Data", href: new_ecommerce_import_path, variant: :primary, size: :sm),
257
- collapsible: true,
258
- collapse_key: "tenant-#{current_tenant.id}-executive-pulse-stale-data"
259
- ) do %>
260
- <p>Latest available data is 10 days old.</p>
261
- <% end %>
262
- ```
263
-
264
- Collapsed alerts stay inline and can be re-expanded with the built-in `More` summary action.
265
-
266
- ## Tables
267
-
268
- Use the table helpers as a small composition system:
269
-
270
- - `ui_table` is the table atom.
271
- - `ui_table_card` is the card shell for title, controls, table body, and footer.
272
- - `ui_table_footer` owns the `Show [x] items per page` control and the `Showing x-y of z` status line.
273
- - `ui_pagination` is the page-navigation atom and is usually composed through `ui_table_footer`.
274
-
275
- If a table has title, controls, rows, and pagination, render them inside the same `ui_table_card`.
276
-
277
- Use `ui_table` directly for embedded or simple tables:
278
-
279
- ```erb
280
- <%= ui_table(
281
- columns: [
282
- { label: "SKU", key: :sku },
283
- { label: "Status", key: :status },
284
- { label: "Revenue", key: :revenue, header_class: "text-right", cell_class: "text-right" }
285
- ],
286
- rows: [
287
- { sku: "SKU-001", status: "Active", revenue: number_to_currency(12_500) },
288
- { sku: "SKU-002", status: "Draft", revenue: number_to_currency(3_800) }
289
- ],
290
- empty_state: "No SKUs found"
291
- ) %>
292
- ```
293
-
294
- Use `ui_table_card` when the table is a standalone surface:
295
-
296
- ```erb
297
- <% table_controls = capture do %>
298
- <div class="flex items-end gap-3">
299
- <%= ui_menu_select_tag :status,
300
- options: [
301
- { label: "All", value: "all" },
302
- { label: "Active", value: "active" },
303
- { label: "Draft", value: "draft" }
304
- ],
305
- selected: params[:status].presence || "all",
306
- label: "Status" %>
307
- </div>
308
- <% end %>
309
-
310
- <%= ui_table_card(
311
- title: "Products",
312
- description: "Track inventory and performance in one place.",
313
- controls: table_controls,
314
- controls_position: :header,
315
- footer: ui_table_footer(
316
- current_page: @pagination[:current_page],
317
- total_pages: @pagination[:total_pages],
318
- total_count: @pagination[:total_count],
319
- per_page: @pagination[:per_page],
320
- path_builder: ->(page) { products_path(request.query_parameters.merge(page: page, per_page: @pagination[:per_page])) },
321
- rows_per_page_param: "per_page",
322
- rows_per_page_options: [10, 20, 50],
323
- rows_per_page_selected: @pagination[:per_page]
324
- )
325
- ) do %>
326
- <%= ui_table(
327
- sort: { key: params[:sort], direction: params[:direction] },
328
- sort_path_builder: ->(sort_key, direction) { products_path(request.query_parameters.merge(sort: sort_key, direction: direction, page: 1)) },
329
- columns: [
330
- { label: "SKU", key: :sku, sortable: true, sort_key: "sku" },
331
- { label: "Status", key: :status },
332
- {
333
- label: "Revenue",
334
- key: :revenue,
335
- sortable: true,
336
- sort_key: "revenue",
337
- header_class: "text-right",
338
- cell_class: "text-right",
339
- header_tooltip: "Total revenue attributed to the current filter window."
340
- }
341
- ],
342
- rows: @rows,
343
- empty_state: "No products found"
344
- ) %>
345
- <% end %>
346
- ```
347
-
348
- Use `controls_position: :header` for compact data-view controls that belong in the top-right header zone. Keep the default `:row` placement for wider filter bars. You can also pass `title_meta:` to render subdued inline metadata beside the title, for example `title_meta: "24 rows"`.
64
+ ## Getting Started
349
65
 
350
- Sorting is opt-in: header sort controls render only when a column is marked `sortable: true` and the table receives `sort:` plus `sort_path_builder:`.
66
+ - See [docs/installation.md](docs/installation.md) for install steps.
67
+ - See [docs/sidebar.md](docs/sidebar.md) and [docs/auth.md](docs/auth.md) for common usage patterns.
351
68
 
352
- Use `ui_pagination` directly only when you need bare page navigation without the table-footer composition.
69
+ ## Documentation
353
70
 
354
- ## Marketing Templates
71
+ - [Installation](docs/installation.md)
72
+ - [Styling](docs/styling.md)
73
+ - [Sidebar](docs/sidebar.md)
74
+ - [Auth](docs/auth.md)
75
+ - [Modals and Panels](docs/modals-and-panels.md)
76
+ - [Alerts and Snackbars](docs/alerts-and-snackbars.md)
77
+ - [Tables](docs/tables.md)
78
+ - [Marketing](docs/marketing.md)
79
+ - [Security](docs/security.md)
355
80
 
356
- Keep marketing-page templates separate from app UI primitives. Baldur’s low-level atoms still live under the shared `ui_*` component layer, while full-page marketing surfaces use dedicated `ui_marketing_*` helpers.
357
-
358
- Current canonical marketing helpers:
359
-
360
- - `ui_marketing_top_nav`
361
- - `ui_marketing_hero_section(variant: :solar_system, ...)`
362
- - `ui_marketing_features_section`
363
- - `ui_marketing_testimonials_section(variant: :bento, ...)`
364
- - `ui_marketing_faq_section`
365
- - `ui_marketing_cta_banner`
366
- - `ui_marketing_pricing_tables`
367
- - `ui_marketing_footer`
81
+ ## Security
368
82
 
369
- Use the existing landing and pricing templates as the canonical v1 variants. Future hero or testimonial layouts should be added as new variants, not folded into the existing default markup.
83
+ See [docs/security.md](docs/security.md) for artifact verification, MFA requirements, and vulnerability reporting.
370
84
 
371
- Marketing nav/footer branding comes from `config.marketing_brand` in the Baldur initializer. Hosts can override that deployment-level default per render by passing `brand:` into `ui_marketing_top_nav` or `ui_marketing_footer`. If a future host needs tenant-specific or whitelabel branding, resolve/cache that in the app and pass the resolved values through `brand:` rather than teaching Baldur about tenant lookup.
85
+ ## Available Now
372
86
 
373
- Interactive marketing templates ship with Baldur-owned Stimulus controllers. `baldur:install` now generates `marketing_tabs_controller.js` and `marketing_pricing_controller.js` shims so features tabs and pricing billing toggles do not depend on host-specific controller names.
87
+ - [x] Host app brand theming via `theme.css` and `config.marketing_brand`
88
+ - [x] Reusable auth page shell for sign-in, sign-up, and password reset
89
+ - [x] Core UI helpers, Tailwind components, and Stimulus wiring
374
90
 
375
- `config.marketing_brand` supports `name`, `wordmark`, `logo_src`, `logo_alt`, and optional `href`. Hosts should treat that config as the canonical branding contract instead of relying on helper-method coupling.
91
+ ## Roadmap
376
92
 
377
- Example hero usage:
93
+ ### Installing
94
+ - Harden `baldur:install` so host assumptions are reduced
95
+ - Expand `script/verify_host_install` coverage
96
+ - Add end-to-end install verification in dummy app
378
97
 
379
- ```erb
380
- <%= ui_marketing_hero_section(
381
- variant: :solar_system,
382
- headline: "Turn Every Data Point Into ROI",
383
- body: "Connect fragmented data into one decision engine.",
384
- primary_action: { label: "Book a Demo", variant: :primary, href: dashboard_path },
385
- secondary_action: { label: "See Use Cases", variant: :outline, href: "#use-cases" },
386
- supporting_action: { href: "#", label: "Watch walkthrough", data: { open_modal: "#walkthrough-modal" } },
387
- callouts: [
388
- { label: "Unified decision context" },
389
- { label: "Prioritized recommendations" },
390
- { label: "Impact-aware next actions" }
391
- ],
392
- orbit_sources: [
393
- { name: "Shopify", asset_path: "/landing/source-logos/shopify.svg" },
394
- { name: "HubSpot", asset_path: "/landing/source-logos/hubspot.svg" }
395
- ],
396
- centerpiece_image: { src: "/branding/logo.png", alt: "Acme logo" }
397
- ) %>
398
- ```
98
+ ### Showcase
99
+ - Add a dedicated dummy app for visual smoke checks
100
+ - Add component inventory/showcase page in dummy app
101
+ - Add interaction showcase pages for modal, sidebar, menu select, snackbar, and panel_secondary
102
+ - Add copy-paste examples for core surfaces from the showcase app back into docs
399
103
 
400
- Example features section usage:
104
+ ### Starter Templates
105
+ - Add dedicated password reset templates
106
+ - Add magic link templates
107
+ - Add error/system pages
108
+ - Add starter settings page layout
109
+ - Add starter CRUD index/show/form templates
110
+ - Add starter dashboard shell/page templates
401
111
 
402
- ```erb
403
- <%= ui_marketing_features_section(
404
- title: "What Mimir unlocks for your teams",
405
- description: "Tailored to your business model and decision priorities.",
406
- tabs: [
407
- {
408
- value: "ecommerce",
409
- label: "E-commerce",
410
- selected: true,
411
- panel_title: "E-commerce",
412
- panel_body: "Priority ROI plays for commerce teams.",
413
- cards: [
414
- { title: "Which products should I run campaigns for?", body: "Rank SKUs by incremental margin potential." }
415
- ]
416
- }
417
- ],
418
- cta: { label: "Get a demo tailored for you", variant: :primary, href: dashboard_path }
419
- ) %>
420
- ```
112
+ ### Forms
113
+ - Add Rails form-builder integration for Baldur fields
114
+ - Bind labels, hints, errors, and invalid states automatically
115
+ - Document model-bound form usage
421
116
 
422
- ## Snackbars
117
+ ### Tables and Resource Screens
118
+ - Add a higher-level resource index pattern on top of existing table primitives
119
+ - Support search, filters, row actions, bulk select, and empty states
120
+ - Document recommended resource index composition
423
121
 
424
- Use semantic snackbar tones:
122
+ ### Theming
123
+ - Add a small set of starter theme presets
124
+ - Improve dark-mode/theme controller documentation
125
+ - Document brand-token customization more clearly
425
126
 
426
- - `:success` for green success states
427
- - `:notice` for blue notice/info states
428
- - `:warning` for amber warning states
429
- - `:error` for red error states
127
+ ### Accessibility
128
+ - Audit keyboard and focus behavior across interactive components
129
+ - Add accessibility-focused tests for core surfaces
130
+ - Document a11y guarantees and known gaps
430
131
 
431
- Host apps should map `flash[:notice]` to `:notice` and `flash[:alert]` to `:error` unless they have a stronger semantic signal available.
132
+ ### Internationalization
133
+ - Add I18n-friendly labels/messages for reusable helpers
134
+ - Remove hard-coded helper copy where practical
135
+ - Document translation override points
432
136
 
433
- ## Smoke Check
137
+ ## Contributing
434
138
 
435
- Run this from the host app root after installation:
139
+ Open an issue or PR. Before you start, read [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/security.md](docs/security.md).
436
140
 
437
- ```sh
438
- bundle exec ruby "$(bundle show baldur)/script/verify_host_install"
439
- ```
141
+ ## Changelog
440
142
 
441
- That verifies the host can render core helpers and confirms the Tailwind entrypoint contains the required Baldur imports.
143
+ See [CHANGELOG.md](CHANGELOG.md).
442
144
 
443
- ## Deferred Work
145
+ ## License
444
146
 
445
- See `TODO.md` for work intentionally deferred until the dedicated gem repo exists.
147
+ MIT. See [LICENSE](LICENSE).
data/TODO.md CHANGED
@@ -1,8 +1,50 @@
1
1
  # Baldur TODO
2
2
 
3
- - Improve RubyGems page description for DX
4
- - Reduce min required versions for Rails and TailwindCSS to maximise support
3
+ ## Install and Verification
4
+ - [ ] Harden `baldur:install` so host assumptions are reduced
5
+ - [ ] Audit generated controller shims against all shipped components
6
+ - [ ] Expand `script/verify_host_install` coverage
7
+ - [ ] Add end-to-end install verification in dummy app
5
8
 
6
- - Add a dedicated dummy app in the extracted gem repo for visual smoke checks.
7
- - Add a component inventory/showcase page in that dummy app.
8
- - Add a few interaction-specific showcase pages for modal, sidebar, menu select, snackbar, and `panel_secondary`.
9
+ ## Showcase App and Docs
10
+ - [ ] Add a dedicated dummy app in the extracted gem repo for visual smoke checks
11
+ - [ ] Add a component inventory/showcase page in that dummy app
12
+ - [ ] Add interaction showcase pages for modal, sidebar, menu select, snackbar, and `panel_secondary`
13
+ - [ ] Add copy-paste examples for core surfaces from the showcase app back into docs
14
+
15
+ ## Starter Templates
16
+ - [ ] Add dedicated password reset templates
17
+ - [ ] Add magic link templates
18
+ - [ ] Add error/system pages
19
+ - [ ] Add starter settings page layout
20
+ - [ ] Add starter CRUD index/show/form templates
21
+ - [ ] Add starter dashboard shell/page templates
22
+
23
+ ## Forms
24
+ - [ ] Add Rails form-builder integration for Baldur fields
25
+ - [ ] Bind labels, hints, errors, and invalid states automatically
26
+ - [ ] Document model-bound form usage
27
+
28
+ ## Tables and Resource Screens
29
+ - [ ] Add a higher-level resource index pattern on top of existing table primitives
30
+ - [ ] Support search, filters, row actions, bulk select, and empty states
31
+ - [ ] Document recommended resource index composition
32
+
33
+ ## Theming
34
+ - [ ] Add a small set of starter theme presets
35
+ - [ ] Improve dark-mode/theme controller documentation
36
+ - [ ] Document brand-token customization more clearly
37
+
38
+ ## Accessibility
39
+ - [ ] Audit keyboard and focus behavior across interactive components
40
+ - [ ] Add accessibility-focused tests for core surfaces
41
+ - [ ] Document a11y guarantees and known gaps
42
+
43
+ ## Internationalization
44
+ - [ ] Add I18n-friendly labels/messages for reusable helpers
45
+ - [ ] Remove hard-coded helper copy where practical
46
+ - [ ] Document translation override points
47
+
48
+ ## Contributor and Agent Guidance
49
+ - [ ] Add `.agents/AGENTS.md` with project conventions and reference files for LLM agents
50
+ - [ ] Keep `.agents/AGENTS.md` in sync with actual project conventions
@@ -0,0 +1,23 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class ConfirmationController extends Controller {
4
+ static targets = ["input", "submit", "expectedText"];
5
+ static values = { caseSensitive: { type: Boolean, default: true } };
6
+
7
+ connect() {
8
+ this.validate();
9
+ }
10
+
11
+ validate() {
12
+ if (!this.hasSubmitTarget || !this.hasInputTarget) return;
13
+
14
+ const input = this.inputTarget.value.trim();
15
+ const expected = this.hasExpectedTextTarget ? this.expectedTextTarget.textContent.trim() : "";
16
+
17
+ const matches = this.caseSensitiveValue
18
+ ? input === expected
19
+ : input.toLowerCase() === expected.toLowerCase();
20
+
21
+ this.submitTarget.disabled = !matches;
22
+ }
23
+ }
@@ -0,0 +1,11 @@
1
+ @layer components {
2
+ .confirmation-modal__input-wrapper {
3
+ margin-top: var(--space-4);
4
+ }
5
+
6
+ [data-confirmation] .text-field__control:focus {
7
+ border-color: var(--color-primary);
8
+ outline: none;
9
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-primary) 24%, transparent);
10
+ }
11
+ }