layered-ui-rails 0.4.0 → 0.6.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: '0596cb85b0ecaf7e9efe534a5d7b7ff9e9c3f0848d1faf8972ce7f1e4617fb1b'
4
- data.tar.gz: f03e28644cd462bcf5988d851d4f85ad177761fd37eea52ed404a4046b230404
3
+ metadata.gz: 8b3fa30f64b14c50db43ea23c4fbdb699903a7f4c2f957a8b22cd85772868a7b
4
+ data.tar.gz: 215e0d57d2403591bf31dd9d31ea05c281e5f36f83ebe822a4ae3aa902eabf0b
5
5
  SHA512:
6
- metadata.gz: 72a5b12ad90bd8997f03e7f9624af1ef3cfeeffc25db54a147c435bde91bb6ed49c353bf049210ec122eae10297db94b96d1918c88131acb13fac1786171feca
7
- data.tar.gz: 150ccf137e7e41cb2181444b11c73cb3bb583ae3e1160b3f87cc70ee019acf34c688af70e6a09ad04b5ff756a5e8f7f72fd4e9afedbb408a052ad6b81cde41ca
6
+ metadata.gz: 890b8e734f1b573143bf40deda9d3d5c64f1eb358f075a0a5e6608475eaeeb6dd0470675e68b90ac72f55f28f7a688bb19410bc99f5f77e3c590c2025c90ab50
7
+ data.tar.gz: 73c86751d4a39d80879c254fa35bae780c74ae4c7a5e45fb9c41c5634a6b69ce60632750e035176439c038646415a98e0b6988f389e8368cf1354348363a4553
@@ -22,9 +22,16 @@ bin/rails generate layered:ui:install
22
22
 
23
23
  The generator copies `layered_ui.css` into `app/assets/tailwind/`, adds the CSS import to `application.css`, and adds the JS import to `application.js`.
24
24
 
25
- Then render the engine layout from your application layout:
25
+ Then render the engine layout from your application layout. Place all `content_for` blocks **above** the render call - the engine layout reads them when it renders, so they must be defined first:
26
26
 
27
27
  ```erb
28
+ <% content_for :l_ui_body_class, "l-ui-body--always-show-navigation" %>
29
+
30
+ <% content_for :l_ui_navigation_items do %>
31
+ <%= l_ui_navigation_item("Dashboard", dashboard_path) %>
32
+ <%= l_ui_navigation_item("Users", users_path) %>
33
+ <% end %>
34
+
28
35
  <%= render template: "layouts/layered_ui/application" %>
29
36
  ```
30
37
 
@@ -34,7 +41,7 @@ The engine layout provides a fixed header (63px), optional sidebar navigation (2
34
41
 
35
42
  ### Content blocks
36
43
 
37
- Populate layout regions with `content_for`:
44
+ Populate layout regions with `content_for` (always above the render call):
38
45
 
39
46
  ```erb
40
47
  <%# Navigation sidebar items %>
@@ -102,6 +109,9 @@ Quick reference:
102
109
  | `l_ui_pagy(pagy)` | Styled pagination (requires pagy gem) |
103
110
  | `l_ui_search_form(query, url:, fields:, ...)` | Search form (requires ransack gem) |
104
111
  | `l_ui_sort_link(query, attribute, label = nil, ...)` | Sortable table header (requires ransack gem) |
112
+ | `l_ui_table(records, columns:, caption:, ...)` | Styled accessible data table with optional sort and actions |
113
+ | `l_ui_form(record, fields:, url:, method:)` | Complete form with fields, error summary, and submit |
114
+ | `l_ui_normalise_field(record, config)` | Normalise a raw field config hash into canonical form |
105
115
  | `l_ui_user_signed_in?` | Check if user is authenticated |
106
116
  | `l_ui_current_user` | Current user object |
107
117
 
@@ -136,7 +146,7 @@ All controllers use the `l-ui--` namespace and are auto-registered via importmap
136
146
  | Panel resize | `l-ui--panel-resize` | Panel width drag handle |
137
147
  | Modal | `l-ui--modal` | Native `<dialog>` with focus trap |
138
148
  | Tabs | `l-ui--tabs` | Accessible tabbed interface |
139
- | Search form | `l-ui--search-form` | Multi-scope search with Turbo support |
149
+ | Search form | `l-ui--search-form` | Multi-scope search with Turbo support and pagination param preservation |
140
150
 
141
151
  ## Theming
142
152
 
@@ -154,7 +164,7 @@ Override CSS custom properties after the engine import. Values are space-separat
154
164
  }
155
165
  ```
156
166
 
157
- Key tokens: `--accent`, `--accent-foreground`, `--background`, `--foreground`, `--foreground-muted`, `--border`, `--border-control`, `--surface`, `--surface-active`, `--danger`, `--header-height`.
167
+ Key tokens: `--accent`, `--accent-foreground`, `--background`, `--foreground`, `--foreground-muted`, `--border`, `--border-control`, `--surface`, `--surface-highlighted`, `--danger`, `--header-height`.
158
168
 
159
169
  ## Asset overrides
160
170
 
@@ -164,7 +174,7 @@ Place files in `app/assets/images/layered_ui/` to replace engine defaults:
164
174
 
165
175
  ## Optional integrations
166
176
 
167
- - **Devise** - auto-detected. Provides styled auth views, header login/register buttons, sidebar user info and logout.
177
+ - **Devise** - auto-detected. Provides styled auth views, header login/register buttons, sidebar user info and logout. Setup: `bundle add devise`, run `devise:install` and `devise User` generators, add `devise_for :users` to routes. Configure `Layered::Ui.current_user_method` if not using `:current_user`. Helpers: `l_ui_devise_installed?`, `l_ui_user_signed_in?`.
168
178
  - **Pagy** - auto-detected. Use `l_ui_pagy(@pagy)` for styled pagination.
169
179
  - **Ransack** - auto-detected. Use `l_ui_search_form` and `l_ui_sort_link` for styled search and sortable tables.
170
180
 
@@ -139,7 +139,7 @@ Drag handle for resizing the panel width on desktop.
139
139
  Manages multi-scope search forms with parameter preservation and Turbo frame support.
140
140
 
141
141
  **Values:** `scope` (String, default `"q"`)
142
- **Actions:** `preserve`, `clear`
142
+ **Actions:** `preserve`, `clear`, `rewriteLink`
143
143
 
144
144
  ```html
145
145
  <form data-controller="l-ui--search-form"
@@ -153,4 +153,17 @@ Manages multi-scope search forms with parameter preservation and Turbo frame sup
153
153
  </form>
154
154
  ```
155
155
 
156
- When multiple search forms exist on one page (each with a different `scope` value), submitting one form automatically preserves the other forms' query parameters.
156
+ When multiple search forms exist on one page (each with a different `scope` value), submitting one form automatically preserves the other forms' query parameters. The `page` param and any scoped page param matching the scope (e.g. `users_page` for scope `users_q`) are reset on submit so pagination returns to page 1.
157
+
158
+ **`rewriteLink`** - merges current URL params into a clicked link's href. Useful for pagination links inside Turbo Frames where the server-rendered href may be missing params from other scopes. Attach to a parent element (e.g. the Turbo Frame):
159
+
160
+ ```html
161
+ &lt;%= turbo_frame_tag "users_collection", data: { turbo_action: "advance",
162
+ controller: "l-ui--search-form", l_ui__search_form_scope_value: "users_q",
163
+ action: "click->l-ui--search-form#rewriteLink" } do %&gt;
164
+ &lt;%= l_ui_search_form(@users_q, url: users_path, fields: [:name, :email],
165
+ clear: true, turbo_frame: "users_collection") %&gt;
166
+ &lt;%= l_ui_table(@users, ..., query: @users_q, turbo_frame: "users_collection") %&gt;
167
+ &lt;%= l_ui_pagy(@users_pagy) %&gt;
168
+ &lt;% end %&gt;
169
+ ```
@@ -13,78 +13,90 @@ All classes use the `l-ui-` prefix with BEM naming. Use these in host app views.
13
13
 
14
14
  ## Buttons
15
15
 
16
+ Standalone variants (use one of these, not combined with each other):
17
+
16
18
  ```
17
- .l-ui-button Base button with padding and focus ring
18
- .l-ui-button--primary Accent-coloured button
19
- .l-ui-button--outline Bordered button
20
- .l-ui-button--outline-danger Red bordered button
21
- .l-ui-button--full Full-width button
22
- .l-ui-button--icon Icon-only button (fixed size, no text)
23
- .l-ui-button--disabled Disabled appearance
19
+ .l-ui-button Plain button with padding and focus ring
20
+ .l-ui-button--primary Accent-coloured solid button
21
+ .l-ui-button--danger Solid red button (for destructive actions)
22
+ .l-ui-button--outline Bordered button
23
+ .l-ui-button--outline-danger Red bordered button (for destructive actions)
24
+ .l-ui-button--icon Icon-only button (fixed size, no text)
24
25
  .l-ui-button--navigation-toggle Mobile navigation toggle
25
- .l-ui-button--panel-close Panel close button
26
+ .l-ui-button--panel-close Panel close button
27
+ ```
28
+
29
+ Modifiers (combine with a standalone variant above):
30
+
31
+ ```
32
+ .l-ui-button--full Full-width (e.g. l-ui-button--primary l-ui-button--full)
26
33
  ```
27
34
 
35
+ Any button variant is automatically styled as disabled when the `disabled` HTML attribute is present - no extra class needed.
36
+
37
+ For destructive actions use `l-ui-button--danger` (solid) or `l-ui-button--outline-danger` (bordered).
38
+
28
39
  ## Surfaces
29
40
 
30
41
  ```
31
- .l-ui-surface Rounded, padded container
32
- .l-ui-surface--active Darker background variant
33
- .l-ui-surface--sm Smaller padding
34
- .l-ui-surface--collapsible Wraps a <details> element
35
- .l-ui-surface--collapsible-active Open state
36
- .l-ui-surface__summary Collapsible toggle (on <summary>)
37
- .l-ui-surface__chevron Chevron indicator (rotates on open)
38
- .l-ui-surface__content Collapsible content area
42
+ .l-ui-surface Rounded, padded container
43
+ .l-ui-surface--highlighted Darker background variant
44
+ .l-ui-surface--sm Smaller padding
45
+ .l-ui-surface--collapsible Wraps a <details> element
46
+ .l-ui-surface--collapsible-highlighted Highlighted variant
47
+ .l-ui-surface__summary Collapsible toggle (on <summary>)
48
+ .l-ui-surface__chevron Chevron indicator (rotates on open)
49
+ .l-ui-surface__content Collapsible content area
39
50
  ```
40
51
 
41
52
  ## Forms
42
53
 
43
54
  ```
44
- .l-ui-form Form container
45
- .l-ui-form__group Vertical field group with spacing
55
+ .l-ui-form Form container
56
+ .l-ui-form__group Vertical field group with spacing
46
57
  .l-ui-form__group--large-gap Larger spacing variant
47
- .l-ui-form__field Input/textarea styling
48
- .l-ui-form__errors Error summary box
49
- .l-ui-form__errors-list Bulleted error list
50
- .l-ui-form__field-error Individual field error message
51
- .l-ui-form__hint Field hint text
52
- .l-ui-form__required Required indicator (*)
58
+ .l-ui-form__field Input/textarea styling
59
+ .l-ui-form__errors Error summary box
60
+ .l-ui-form__errors-list Bulleted error list
61
+ .l-ui-form__actions Right-aligned action button container (full-width buttons on mobile)
62
+ .l-ui-form__field-error Individual field error message
63
+ .l-ui-form__hint Field hint text
64
+ .l-ui-form__required Required indicator (*)
53
65
 
54
- .l-ui-label Form label
55
- .l-ui-label--checkbox Checkbox label variant
66
+ .l-ui-label Form label
67
+ .l-ui-label--checkbox Checkbox label variant
56
68
 
57
- .l-ui-select Select dropdown
58
- .l-ui-select-wrapper Select wrapper (custom arrow)
69
+ .l-ui-select Select dropdown
70
+ .l-ui-select-wrapper Select wrapper (custom arrow)
59
71
 
60
- .l-ui-search__inline Inline search form layout
72
+ .l-ui-search__inline Inline search form layout
61
73
 
62
- .l-ui-container--checkbox Checkbox container
63
- .l-ui-radio__group Radio button group
64
- .l-ui-radio__item Radio item wrapper
65
- .l-ui-radio__input Radio input element
66
- .l-ui-radio__label Radio label
74
+ .l-ui-container--checkbox Checkbox container
75
+ .l-ui-radio__group Radio button group
76
+ .l-ui-radio__item Radio item wrapper
77
+ .l-ui-radio__input Radio input element
78
+ .l-ui-radio__label Radio label
67
79
 
68
- .l-ui-switch Toggle switch container
69
- .l-ui-switch__input Hidden checkbox input
70
- .l-ui-switch__track Visual track element
80
+ .l-ui-switch Toggle switch container
81
+ .l-ui-switch__input Hidden checkbox input
82
+ .l-ui-switch__track Visual track element
71
83
  ```
72
84
 
73
85
  ## Tables
74
86
 
75
87
  ```
76
- .l-ui-table Table element
77
- .l-ui-table__header <thead> row
78
- .l-ui-table__header-cell <th> cell
79
- .l-ui-table__header-cell--action Right-aligned action header
80
- .l-ui-table__sort-link Sortable header link
81
- .l-ui-table__sort-indicator Sort direction indicator (arrow)
82
- .l-ui-table__body <tbody>
83
- .l-ui-table__cell Regular <td> cell
84
- .l-ui-table__cell--primary Bold cell (typically first column, use <th scope="row">)
85
- .l-ui-table__cell--action Right-aligned action cell
86
- .l-ui-table__action--danger Danger action link
87
- .l-ui-container--table Overflow wrapper for responsive tables
88
+ .l-ui-table Table element
89
+ .l-ui-table__header <thead> row
90
+ .l-ui-table__header-cell <th> cell
91
+ .l-ui-table__header-cell--action Right-aligned action header
92
+ .l-ui-table__sort-link Sortable header link
93
+ .l-ui-table__sort-indicator Sort direction indicator (arrow)
94
+ .l-ui-table__body <tbody>
95
+ .l-ui-table__cell Regular <td> cell
96
+ .l-ui-table__cell--primary Bold cell (typically first column, use <th scope="row">)
97
+ .l-ui-table__cell--action Right-aligned action cell
98
+ .l-ui-table__action--danger Danger action link
99
+ .l-ui-container--table Overflow wrapper for responsive tables
88
100
  ```
89
101
 
90
102
  WCAG 2.2 AA table pattern:
@@ -116,126 +128,126 @@ WCAG 2.2 AA table pattern:
116
128
  ## Notices
117
129
 
118
130
  ```
119
- .l-ui-notice--success Green success message
120
- .l-ui-notice--warning Yellow warning message
121
- .l-ui-notice--error Red error message
131
+ .l-ui-notice--success Green success message
132
+ .l-ui-notice--warning Yellow warning message
133
+ .l-ui-notice--error Red error message
122
134
  ```
123
135
 
124
136
  ## Badges
125
137
 
126
138
  ```
127
- .l-ui-badge Base badge
128
- .l-ui-badge--rounded Pill shape
129
- .l-ui-badge--default Grey
130
- .l-ui-badge--success Green
131
- .l-ui-badge--warning Yellow
132
- .l-ui-badge--danger Red
139
+ .l-ui-badge Base badge
140
+ .l-ui-badge--rounded Pill shape
141
+ .l-ui-badge--default Grey
142
+ .l-ui-badge--success Green
143
+ .l-ui-badge--warning Yellow
144
+ .l-ui-badge--danger Red
133
145
  ```
134
146
 
135
147
  ## Tabs
136
148
 
137
149
  ```
138
- .l-ui-tabs__list Tab list container (role="tablist")
139
- .l-ui-tabs__tab Tab button
140
- .l-ui-tabs__tab--active Active tab with accent border
141
- .l-ui-tabs__panel Tab panel content
150
+ .l-ui-tabs__list Tab list container (role="tablist")
151
+ .l-ui-tabs__tab Tab button
152
+ .l-ui-tabs__tab--active Active tab with accent border
153
+ .l-ui-tabs__panel Tab panel content
142
154
  ```
143
155
 
144
156
  ## Modal
145
157
 
146
158
  ```
147
- .l-ui-modal <dialog> element
148
- .l-ui-modal__header Modal header
149
- .l-ui-modal__body Scrollable modal content
159
+ .l-ui-modal <dialog> element
160
+ .l-ui-modal__header Modal header
161
+ .l-ui-modal__body Scrollable modal content
150
162
  ```
151
163
 
152
164
  ## Breadcrumbs
153
165
 
154
166
  ```
155
- .l-ui-breadcrumbs <nav> container
156
- .l-ui-breadcrumbs__list <ol> list
157
- .l-ui-breadcrumbs__item <li> item
158
- .l-ui-breadcrumbs__link Breadcrumb link
167
+ .l-ui-breadcrumbs <nav> container
168
+ .l-ui-breadcrumbs__list <ol> list
169
+ .l-ui-breadcrumbs__item <li> item
170
+ .l-ui-breadcrumbs__link Breadcrumb link
159
171
  ```
160
172
 
161
173
  ## Pagination
162
174
 
163
175
  ```
164
- .l-ui-pagination Pagination container
165
- .l-ui-pagination__item Page link or span
166
- .l-ui-pagination__item--active Current page
167
- .l-ui-pagination__item--disabled Disabled navigation
168
- .l-ui-pagination__gap Gap indicator (...)
176
+ .l-ui-pagination Pagination container
177
+ .l-ui-pagination__item Page link or span
178
+ .l-ui-pagination__item--active Current page
179
+ .l-ui-pagination__item--disabled Disabled navigation
180
+ .l-ui-pagination__gap Gap indicator (...)
169
181
  ```
170
182
 
171
183
  ## Navigation
172
184
 
173
185
  ```
174
- .l-ui-container--navigation Sidebar container
175
- .l-ui-container--navigation.open Visible sidebar
176
- .l-ui-backdrop--navigation Overlay backdrop
177
- .l-ui-backdrop--navigation.open Visible backdrop
178
- .l-ui-navigation Nav flexbox
179
- .l-ui-navigation__links Nav links list
180
- .l-ui-navigation__item Nav item
181
- .l-ui-navigation__item--active Active nav item (with arrow)
182
- .l-ui-navigation__secondary Nested nav list
183
- .l-ui-navigation__user User info section
184
- .l-ui-navigation__user-name User name text
185
- .l-ui-navigation__user-email User email text
186
+ .l-ui-container--navigation Sidebar container
187
+ .l-ui-container--navigation.open Visible sidebar
188
+ .l-ui-backdrop--navigation Overlay backdrop
189
+ .l-ui-backdrop--navigation.open Visible backdrop
190
+ .l-ui-navigation Nav flexbox
191
+ .l-ui-navigation__links Nav links list
192
+ .l-ui-navigation__item Nav item
193
+ .l-ui-navigation__item--active Active nav item (with arrow)
194
+ .l-ui-navigation__secondary Nested nav list
195
+ .l-ui-navigation__user User info section
196
+ .l-ui-navigation__user-name User name text
197
+ .l-ui-navigation__user-email User email text
186
198
  ```
187
199
 
188
200
  ## Header
189
201
 
190
202
  ```
191
- .l-ui-container--header Fixed header container
192
- .l-ui-header Header flexbox
193
- .l-ui-header__icon Header icon (responsive)
194
- .l-ui-header__icon--light Light theme icon
195
- .l-ui-header__icon--dark Dark theme icon
196
- .l-ui-header__logo Header logo (responsive)
197
- .l-ui-header__logo--light Light theme logo
198
- .l-ui-header__logo--dark Dark theme logo
199
- .l-ui-theme-toggle Theme toggle button
200
- .l-ui-theme-toggle__icon--light Sun icon (shown in dark mode)
201
- .l-ui-theme-toggle__icon--dark Moon icon (shown in light mode)
203
+ .l-ui-container--header Fixed header container
204
+ .l-ui-header Header flexbox
205
+ .l-ui-header__icon Header icon (responsive)
206
+ .l-ui-header__icon--light Light theme icon
207
+ .l-ui-header__icon--dark Dark theme icon
208
+ .l-ui-header__logo Header logo (responsive)
209
+ .l-ui-header__logo--light Light theme logo
210
+ .l-ui-header__logo--dark Dark theme logo
211
+ .l-ui-theme-toggle Theme toggle button
212
+ .l-ui-theme-toggle__icon--light Sun icon (shown in dark mode)
213
+ .l-ui-theme-toggle__icon--dark Moon icon (shown in light mode)
202
214
  ```
203
215
 
204
216
  ## Panel
205
217
 
206
218
  ```
207
- .l-ui-container--panel Side panel container
208
- .l-ui-container--panel.open Visible panel
209
- .l-ui-panel Panel flexbox
210
- .l-ui-panel__button Floating action button
211
- .l-ui-panel__button--dragging During drag
212
- .l-ui-panel__button--snapping Snapping to edge
213
- .l-ui-panel__icon--light Panel button icon (light)
214
- .l-ui-panel__icon--dark Panel button icon (dark)
215
- .l-ui-panel__resize-handle Desktop resize handle
216
- .l-ui-panel__header Panel header
217
- .l-ui-panel__header-heading Panel title
218
- .l-ui-panel__body Scrollable panel content
219
- .l-ui-panel__input Panel input area (footer)
219
+ .l-ui-container--panel Side panel container
220
+ .l-ui-container--panel.open Visible panel
221
+ .l-ui-panel Panel flexbox
222
+ .l-ui-panel__button Floating action button
223
+ .l-ui-panel__button--dragging During drag
224
+ .l-ui-panel__button--snapping Snapping to edge
225
+ .l-ui-panel__icon--light Panel button icon (light)
226
+ .l-ui-panel__icon--dark Panel button icon (dark)
227
+ .l-ui-panel__resize-handle Desktop resize handle
228
+ .l-ui-panel__header Panel header
229
+ .l-ui-panel__header-heading Panel title
230
+ .l-ui-panel__body Scrollable panel content
231
+ .l-ui-panel__input Panel input area (footer)
220
232
  ```
221
233
 
222
234
  ## Conversation
223
235
 
224
236
  ```
225
- .l-ui-conversation Conversation wrapper
226
- .l-ui-conversation__messages Scrollable messages area
227
- .l-ui-conversation__composer Message input area
237
+ .l-ui-conversation Conversation wrapper
238
+ .l-ui-conversation__messages Scrollable messages area
239
+ .l-ui-conversation__composer Message input area
228
240
  .l-ui-conversation__composer-input Textarea
229
- .l-ui-conversation__separator Date separator
241
+ .l-ui-conversation__separator Date separator
230
242
 
231
- .l-ui-message Message wrapper
232
- .l-ui-message--sent Sent message (right-aligned)
233
- .l-ui-message__avatar User avatar
234
- .l-ui-message__bubble Message bubble
235
- .l-ui-message__author Author name
236
- .l-ui-message__body Message content
237
- .l-ui-message__footer Metadata footer
238
- .l-ui-message__timestamp Timestamp
243
+ .l-ui-message Message wrapper
244
+ .l-ui-message--sent Sent message (right-aligned)
245
+ .l-ui-message__avatar User avatar
246
+ .l-ui-message__bubble Message bubble
247
+ .l-ui-message__author Author name
248
+ .l-ui-message__body Message content
249
+ .l-ui-message__footer Metadata footer
250
+ .l-ui-message__timestamp Timestamp
239
251
  ```
240
252
 
241
253
  ## Markdown
@@ -256,36 +268,43 @@ WCAG 2.2 AA table pattern:
256
268
  ## Utility classes
257
269
 
258
270
  ```
259
- .l-ui-utility--mt-0 through --mt-8 Margin top (fixed scale)
260
- .l-ui-utility--mt-sm/md/lg/xl/2xl Responsive margin top
261
- .l-ui-utility--mb-0 Margin bottom zero
262
- .l-ui-sr-only Visually hidden, screen reader only
263
- .l-ui-skip-link Accessibility skip link
264
- .l-ui-list Styled list
265
- .l-ui-container--grid 1-col mobile, 2-col desktop grid
266
- .l-ui-container--spread Flex row with space-between
267
- .l-ui-container--pagy Pagination wrapper
268
- .l-ui-scroll-lock Prevent body scroll (mobile panels/modals)
271
+ .l-ui-utility--mt-0 through --mt-8 Margin top (fixed scale)
272
+ .l-ui-utility--mt-sm/md/lg/xl/2xl Responsive margin top
273
+ .l-ui-utility--mb-0 Margin bottom zero
274
+ .l-ui-sr-only Visually hidden, screen reader only
275
+ .l-ui-skip-link Accessibility skip link
276
+ .l-ui-list Styled list
277
+ .l-ui-container--grid 1-col mobile, 2-col desktop grid
278
+ .l-ui-container--spread Flex row with space-between
279
+ .l-ui-container--pagy Pagination wrapper
280
+ .l-ui-scroll-lock Prevent body scroll (mobile panels/modals)
269
281
  ```
270
282
 
271
283
  ## Theming tokens
272
284
 
273
- All colour values are space-separated HSL channels (e.g. `220 80% 55%`). Override after importing the engine CSS.
285
+ All color values are space-separated HSL channels (e.g. `220 80% 55%`). Override after importing the engine CSS.
274
286
 
287
+ Tier 1 - Accent (quick branding):
288
+ ```
289
+ --accent Primary action color
290
+ --accent-foreground Text on accent backgrounds
291
+ ```
292
+
293
+ Tier 2 - Full palette (override individually as needed):
275
294
  ```
276
- --accent Primary action colour
277
- --accent-foreground Text on accent backgrounds
278
295
  --background Page background
279
- --foreground Primary text colour
296
+ --foreground Primary text color
280
297
  --foreground-muted Secondary/muted text
281
- --border Default border colour
298
+ --border Default border color
282
299
  --border-control Form control border
283
- --ring Focus ring colour
300
+ --ring Focus ring color
284
301
  --surface Card/surface background
285
- --surface-active Active/selected surface
286
- --danger Danger/error colour
302
+ --surface-highlighted Highlighted surface
303
+ --button-primary-bg Primary button background (defaults to --accent)
304
+ --button-primary-text Primary button and floating icon text (defaults to --accent-foreground)
305
+ --danger Danger/error color
287
306
  --danger-light Light danger background
288
- --danger-text Danger text colour
307
+ --danger-text Danger text color
289
308
  --success-bg Success background
290
309
  --success-text Success text
291
310
  --switch-track-checked Checked switch track
@@ -293,6 +312,7 @@ All colour values are space-separated HSL channels (e.g. `220 80% 55%`). Overrid
293
312
  --warning-text Warning text
294
313
  --error-bg Error background
295
314
  --error-text Error text
296
- --backdrop Backdrop overlay colour
297
315
  --header-height Header height (default 63px)
298
316
  ```
317
+
318
+ Override --button-primary-text when your accent color needs a different text/icon color on buttons (e.g. a pink accent with white button text in dark mode).
@@ -120,6 +120,87 @@ Returns a `<th>` element with sort link and ARIA sort attributes.
120
120
  </table>
121
121
  ```
122
122
 
123
+ ## Table
124
+
125
+ ```ruby
126
+ l_ui_table(records, columns:, caption: nil, actions: nil,
127
+ actions_label: "Actions", query: nil, url: nil, turbo_frame: nil)
128
+ ```
129
+
130
+ - `records` (ActiveRecord::Relation or Array) - the collection to render
131
+ - `columns` (Array<Hash>) - column definitions (see below)
132
+ - `caption` (String, optional) - visually hidden table caption for accessibility
133
+ - `actions` (Proc, optional) - receives (record), returns action cell content
134
+ - `actions_label` (String) - header text for the actions column, default "Actions"
135
+ - `query` (Ransack::Search, optional) - enables sortable column headers
136
+ - `url` (String, optional) - sort link URL (passed to `l_ui_sort_link`)
137
+ - `turbo_frame` (String, optional) - turbo frame target for sort links
138
+
139
+ Column options:
140
+ - `attribute` (Symbol) - used for label generation and sort links
141
+ - `label` (String, optional) - custom header text; defaults to humanised attribute
142
+ - `primary` (Boolean, optional) - renders as `<th scope="row">`; defaults to first column
143
+ - `sortable` (Boolean, optional) - show sort link when `query:` is provided; defaults to true
144
+ - `render` (Proc, required) - receives (record), returns cell content
145
+
146
+ ### `l_ui_format_datetime(value)`
147
+
148
+ Formats a date/time value as `"%-d %b %Y, %H:%M"` (e.g. "15 Apr 2026, 10:30"). Returns `nil` for `nil` input. Useful inside `render:` procs for date columns.
149
+
150
+ ```erb
151
+ <%= l_ui_table(@users,
152
+ columns: [
153
+ { attribute: :name, primary: true, render: ->(r) { link_to r.name, user_path(r) } },
154
+ { attribute: :email, render: ->(r) { r.email } },
155
+ { attribute: :created_at, label: "Joined", render: ->(r) { l_ui_format_datetime(r.created_at) } },
156
+ ],
157
+ actions: ->(r) { link_to "Edit", edit_user_path(r) },
158
+ caption: "Users",
159
+ query: @q,
160
+ turbo_frame: "users") %>
161
+ ```
162
+
163
+ ## Form
164
+
165
+ ```ruby
166
+ l_ui_form(record, fields:, url:, method: nil)
167
+ ```
168
+
169
+ - `record` (ActiveRecord) - the model instance
170
+ - `fields` (Array<Hash>) - field definitions (see below)
171
+ - `url` (String) - form action URL
172
+ - `method` (Symbol, optional) - HTTP method override
173
+
174
+ Renders a complete form with all fields, error summary, and submit button via the `layered/ui/managed_resource/form` partial.
175
+
176
+ Field options:
177
+ - `attribute` (Symbol) - model attribute
178
+ - `as` (Symbol, optional) - field type; auto-detected from column type. Supported: `:string`, `:text`, `:email`, `:number`, `:date`, `:datetime`, `:select`, `:checkbox`, `:hidden`
179
+ - `label` (String, optional) - custom label text; defaults to humanised attribute
180
+ - `required` (Boolean, optional) - marks field as required; default false
181
+ - `hint` (String, optional) - help text below the field
182
+ - `collection` (Array, optional) - required for `:select` type; e.g. `[['Label', value], ...]`
183
+ - `placeholder` (String, optional) - input placeholder text
184
+
185
+ ```erb
186
+ <%= l_ui_form(@post,
187
+ fields: [
188
+ { attribute: :title, required: true },
189
+ { attribute: :body, as: :text },
190
+ { attribute: :category, as: :select, collection: Category.pluck(:name, :id) },
191
+ { attribute: :published, as: :checkbox },
192
+ ],
193
+ url: posts_path) %>
194
+ ```
195
+
196
+ ### Form utility helpers
197
+
198
+ ```ruby
199
+ l_ui_normalise_field(record, config) # Normalise a raw field config into canonical form
200
+ l_ui_field_error_id(record, attribute) # Error element ID for aria-describedby
201
+ l_ui_field_hint_id(record, attribute) # Hint element ID for aria-describedby
202
+ ```
203
+
123
204
  ## Authentication
124
205
 
125
206
  ```ruby
data/AGENTS.md CHANGED
@@ -5,7 +5,7 @@ Guidance for AI agents working in this repository.
5
5
  ## Architecture
6
6
 
7
7
  - **Entry:** `require "layered-ui-rails"` → `lib/layered/ui.rb` → `lib/layered/ui/engine.rb`
8
- - **Engine:** importmap, assets, Pagy helpers when present; helpers: `AuthenticationHelper`, `NavigationHelper`, `PaginationHelper`
8
+ - **Engine:** importmap, assets, Pagy helpers when present; helpers: `AuthenticationHelper`, `NavigationHelper`, `PagyHelper`
9
9
  - **CSS** `app/assets/tailwind/layered/ui/styles.css`: HSL tokens, `.dark` on `<html>`, `@theme` utilities (`bg-background`, etc.), BEM components (`.l-ui-button--primary`, etc.). Layout: 63px header, 240px sidebar, 320px panel. WCAG 2.2 AA.
10
10
  - **CSS `@apply`:** Multi-line with grouping (layout → spacing → typography → colors → effects). Single utilities may stay on one line.
11
11
  - **Generators:** `bin/rails generate layered:ui:install` (copy CSS, import CSS, import JS)
@@ -23,7 +23,7 @@ Guidance for AI agents working in this repository.
23
23
  - Use normal dashes '-' not long ones '—'
24
24
  - Locale: Favour en-GB (British English), unless terms are defined by technical standards (e.g. LICENSE, COLOR).
25
25
  - Titles: capitalise first word only (e.g. "This title")
26
- - Document new styles in the dummy app
26
+ - Document new styles in the dummy app. When adding a new CSS class or helper, add a working example to the relevant dummy app documentation page (in `test/dummy/app/views/pages/`) following the existing style (heading, description paragraph, live example, "View code" modal). Update the relevant skill reference file in `.claude/skills/layered-ui-rails/references/` to include the new class or helper.
27
27
  - Use l-ui classes only in engine views, with no additional Tailwind utilities, as Tailwind classes referenced only inside the engine will not be generated by the host app’s build
28
28
  - Dummy app documentation pages may use additional Tailwind utilities, but should favour l-ui classes where possible
29
29
  - Importmap for JS (no bundler)