Skip to content

Commit d644a63

Browse files
BEM Refactor (#75)
1 parent 68a613a commit d644a63

57 files changed

Lines changed: 753 additions & 669 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/skills/layered-ui-rails/SKILL.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,14 @@ Key components:
125125

126126
| Component | Key classes |
127127
|---|---|
128-
| Page layout | `.l-ui-page`, `--with-navigation`, `--vertically-centered`, `--width-constrained` |
128+
| Page layout | `.l-ui-page`, `--with-navigation`, `__vertically-centered`, `__width-constrained` |
129129
| Buttons | `.l-ui-button`, `--primary`, `--outline`, `--outline-danger`, `--full`, `--icon` |
130-
| Surfaces | `.l-ui-surface`, `--active`, `--sm`, `--collapsible` |
130+
| Surfaces | `.l-ui-surface`, `--highlighted`, `--sm`, `--collapsible`, `--collapsible-highlighted` |
131131
| Forms | `.l-ui-form`, `.l-ui-form__group`, `.l-ui-form__field`, `.l-ui-label`, `.l-ui-select` |
132-
| Tables | `.l-ui-table`, `.l-ui-table__header`, `.l-ui-table__cell`, `--primary`, `--action` |
132+
| Tables | `.l-ui-table`, `.l-ui-table__header`, `.l-ui-table__cell`, `--primary`, `--action`, `.l-ui-table__action`, `--danger` |
133133
| Badges | `.l-ui-badge`, `--rounded`, `--default`, `--success`, `--warning`, `--danger` |
134134
| Notices | `.l-ui-notice` (base), `--success`, `--warning`, `--error` |
135-
| Tabs | `.l-ui-tabs__list`, `.l-ui-tabs__tab`, `--active` |
135+
| Tabs | `.l-ui-tabs`, `.l-ui-tabs__list`, `.l-ui-tabs__tab`, `--active` |
136136
| Modal | `.l-ui-modal`, `.l-ui-modal__header`, `.l-ui-modal__body` |
137137

138138
## Stimulus controllers

.claude/skills/layered-ui-rails/references/CONTROLLERS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Native `<dialog>` wrapper with focus trap, scroll lock, and focus restoration.
5656
<dialog data-l-ui--modal-target="dialog" class="l-ui-modal">
5757
<div class="l-ui-modal__header">
5858
<h2>Title</h2>
59-
<button data-action="click->l-ui--modal#close" class="l-ui-button--icon">
59+
<button data-action="click->l-ui--modal#close" class="l-ui-button l-ui-button--icon">
6060
Close
6161
</button>
6262
</div>
@@ -82,7 +82,7 @@ Accessible tabbed interface with keyboard navigation.
8282
**Keyboard:** Arrow Left/Right, Home, End
8383

8484
```html
85-
<div data-controller="l-ui--tabs">
85+
<div class="l-ui-tabs" data-controller="l-ui--tabs">
8686
<div role="tablist" class="l-ui-tabs__list">
8787
<button role="tab" data-l-ui--tabs-target="tab"
8888
data-action="click->l-ui--tabs#select keydown->l-ui--tabs#keydown"

.claude/skills/layered-ui-rails/references/CSS.md

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,49 @@
22

33
All classes use the `l-ui-` prefix with BEM naming. Use these in host app views. Do not use raw Tailwind utilities for engine-provided patterns as they will not be generated by the host app's Tailwind build.
44

5+
## Naming conventions
6+
7+
Two patterns deviate from strict BEM by design - leave them in place rather than "fixing" them into elements:
8+
9+
**`*-container` shells.** Several blocks have a sibling `*-container` class (`l-ui-header-container`, `l-ui-navigation-container`, `l-ui-panel-container`, `l-ui-table-container`, `l-ui-pagy-container`, `l-ui-checkbox-container`, `l-ui-select-container`). These are positioning shells responsible for fixed/overlay placement, scroll boundaries, and click-outside scopes. They are treated as their own blocks rather than `__container` elements because they have distinct responsibilities from the inner block, are sometimes used independently in tests/docs, and several BEM dialects accept this. Use the `*-container` suffix consistently for any new shell of this kind.
10+
11+
**Bare-link styling.** `.l-ui-page` and `.l-ui-panel__body` style descendant `<a>` elements that do not already carry an `l-ui-` class:
12+
13+
```css
14+
.l-ui-page a:not([class*="l-ui-"]),
15+
.l-ui-panel__body a:not([class*="l-ui-"]) { ... }
16+
```
17+
18+
This gives author-written links inside long-form content (Markdown, prose, ad-hoc views) consistent underline/colour treatment without forcing an explicit class on every link. Engine elements opt out automatically because their class names contain `l-ui-`. Side effect: any host-app class containing the substring `l-ui-` will also opt an `<a>` out of bare-link styling.
19+
520
## Page layout
621

722
```
823
.l-ui-page Main content wrapper with responsive padding
924
.l-ui-page--with-navigation Left margin for sidebar on desktop
10-
.l-ui-page--vertically-centered Centred layout (e.g. login pages)
11-
.l-ui-page--width-constrained Max-width container
25+
.l-ui-page__vertically-centered Centred layout element (e.g. login pages)
26+
.l-ui-page__width-constrained Max-width container element
1227
```
1328

1429
## Buttons
1530

16-
Standalone variants (use one of these, not combined with each other):
31+
Always combine the `l-ui-button` base class with a colour modifier (e.g. `l-ui-button l-ui-button--primary`):
1732

1833
```
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)
25-
.l-ui-button--navigation-toggle Mobile navigation toggle
26-
.l-ui-button--panel-close Panel close button
34+
.l-ui-button Base class (required with the modifiers below)
35+
.l-ui-button--primary Accent-coloured solid button
36+
.l-ui-button--danger Solid red button (for destructive actions)
37+
.l-ui-button--outline Bordered button
38+
.l-ui-button--outline-danger Red bordered button (for destructive actions)
39+
.l-ui-button--full Full-width (e.g. l-ui-button l-ui-button--primary l-ui-button--full)
40+
.l-ui-button--small Compact 32px-tall variant
2741
```
2842

29-
Modifiers (combine with a standalone variant above):
43+
Icon variants (combine with the `l-ui-button` base):
3044

3145
```
32-
.l-ui-button--full Full-width (e.g. l-ui-button--primary l-ui-button--full)
33-
.l-ui-button--small Compact 32px-tall variant (e.g. l-ui-button--primary l-ui-button--small)
46+
.l-ui-button--icon Icon-only button (fixed size, no text)
47+
.l-ui-button--navigation-toggle Mobile navigation toggle
3448
```
3549

3650
Any button variant is automatically styled as disabled when the `disabled` HTML attribute is present - no extra class needed.
@@ -39,12 +53,14 @@ For destructive actions use `l-ui-button--danger` (solid) or `l-ui-button--outli
3953

4054
## Surfaces
4155

56+
Always combine the `l-ui-surface` base class with any modifiers (e.g. `l-ui-surface l-ui-surface--highlighted`):
57+
4258
```
43-
.l-ui-surface Rounded, padded container
59+
.l-ui-surface Base class (required with the modifiers below)
4460
.l-ui-surface--highlighted Darker background variant
4561
.l-ui-surface--sm Smaller padding
4662
.l-ui-surface--collapsible Wraps a <details> element
47-
.l-ui-surface--collapsible-highlighted Highlighted variant
63+
.l-ui-surface--collapsible-highlighted Highlighted collapsible variant
4864
.l-ui-surface__summary Collapsible toggle (on <summary>)
4965
.l-ui-surface__chevron Chevron indicator (rotates on open)
5066
.l-ui-surface__content Collapsible content area
@@ -55,7 +71,7 @@ For destructive actions use `l-ui-button--danger` (solid) or `l-ui-button--outli
5571
```
5672
.l-ui-form Form container
5773
.l-ui-form__group Vertical field group with spacing
58-
.l-ui-form__group--large-gap Larger spacing variant
74+
.l-ui-form__group--large-gap Larger spacing modifier (combine with l-ui-form__group)
5975
.l-ui-form__field Input/textarea styling
6076
.l-ui-form__errors Error summary box
6177
.l-ui-form__errors-list Bulleted error list
@@ -65,14 +81,16 @@ For destructive actions use `l-ui-button--danger` (solid) or `l-ui-button--outli
6581
.l-ui-form__required Required indicator (*)
6682
6783
.l-ui-label Form label
68-
.l-ui-label--checkbox Checkbox label variant
84+
85+
.l-ui-checkbox-container Checkbox row (input + label)
86+
.l-ui-checkbox Checkbox input
87+
.l-ui-checkbox-container__label Checkbox label element
6988
7089
.l-ui-select Select dropdown
71-
.l-ui-select-wrapper Select wrapper (custom arrow)
90+
.l-ui-select-container Select wrapper (custom arrow)
7291
7392
.l-ui-search-inline Inline search form layout
7493
75-
.l-ui-checkbox-container Checkbox container
7694
.l-ui-radio Radio button group
7795
.l-ui-radio__item Radio item wrapper
7896
.l-ui-radio__input Radio input element
@@ -90,13 +108,15 @@ For destructive actions use `l-ui-button--danger` (solid) or `l-ui-button--outli
90108
.l-ui-table__header <thead> row
91109
.l-ui-table__header-cell <th> cell
92110
.l-ui-table__header-cell--action Right-aligned action header
111+
.l-ui-table__header-cell--sortable Marker class for sortable headers (no engine styles; host-app/JS hook)
93112
.l-ui-table__sort-link Sortable header link
94113
.l-ui-table__sort-indicator Sort direction indicator (arrow)
95114
.l-ui-table__body <tbody>
96115
.l-ui-table__cell Regular <td> cell
97116
.l-ui-table__cell--primary Bold cell (typically first column, use <th scope="row">)
98117
.l-ui-table__cell--action Right-aligned action cell
99-
.l-ui-table__action--danger Danger action link
118+
.l-ui-table__action Action link/button (inside a __cell--action)
119+
.l-ui-table__action--danger Danger modifier (combine with l-ui-table__action)
100120
.l-ui-table-container Overflow wrapper for responsive tables
101121
```
102122

@@ -110,14 +130,14 @@ WCAG 2.2 AA table pattern:
110130
<tr>
111131
<th scope="col" class="l-ui-table__header-cell">Name</th>
112132
<th scope="col" class="l-ui-table__header-cell">Email</th>
113-
<th scope="col" class="l-ui-table__header-cell--action">Actions</th>
133+
<th scope="col" class="l-ui-table__header-cell l-ui-table__header-cell--action">Actions</th>
114134
</tr>
115135
</thead>
116136
<tbody class="l-ui-table__body">
117137
<tr>
118-
<th scope="row" class="l-ui-table__cell--primary">Alice</th>
138+
<th scope="row" class="l-ui-table__cell l-ui-table__cell--primary">Alice</th>
119139
<td class="l-ui-table__cell">alice@example.com</td>
120-
<td class="l-ui-table__cell--action">
140+
<td class="l-ui-table__cell l-ui-table__cell--action">
121141
<a href="/users/1/edit">Edit</a>
122142
</td>
123143
</tr>
@@ -140,20 +160,23 @@ Always combine the base block with one variant modifier (e.g. `class="l-ui-notic
140160
## Badges
141161

142162
```
143-
.l-ui-badge Base badge
163+
.l-ui-badge Base badge (required alongside a colour modifier)
144164
.l-ui-badge--rounded Pill shape
145165
.l-ui-badge--default Grey
146166
.l-ui-badge--success Green
147167
.l-ui-badge--warning Yellow
148168
.l-ui-badge--danger Red
149169
```
150170

171+
Always combine the base block with a modifier, e.g. `<span class="l-ui-badge l-ui-badge--success">`.
172+
151173
## Tabs
152174

153175
```
176+
.l-ui-tabs Tabs wrapper (data-controller="l-ui--tabs")
154177
.l-ui-tabs__list Tab list container (role="tablist")
155-
.l-ui-tabs__tab Tab button
156-
.l-ui-tabs__tab--active Active tab with accent border
178+
.l-ui-tabs__tab Tab button (base class, required)
179+
.l-ui-tabs__tab--active Active tab with accent border (combine with l-ui-tabs__tab)
157180
.l-ui-tabs__panel Tab panel content
158181
```
159182

@@ -196,22 +219,22 @@ Always combine the base block with one variant modifier (e.g. `class="l-ui-notic
196219
## Navigation
197220

198221
```
199-
.l-ui-navigation-container Sidebar container
200-
.l-ui-navigation-container.open Visible sidebar
201-
.l-ui-backdrop Base backdrop (always pair with a variant)
202-
.l-ui-backdrop--navigation Navigation overlay (use with .l-ui-backdrop)
203-
.l-ui-backdrop.open Visible backdrop
222+
.l-ui-navigation-container Sidebar container
223+
.l-ui-navigation-container--open Visible sidebar (toggled by Stimulus)
224+
.l-ui-backdrop Base backdrop (always pair with a variant)
225+
.l-ui-backdrop--navigation Navigation overlay (use with .l-ui-backdrop)
226+
.l-ui-backdrop--open Visible backdrop (toggled by Stimulus)
204227
.l-ui-navigation Nav flexbox
205228
.l-ui-navigation__links Nav links list
206-
.l-ui-navigation__item Nav item
207-
.l-ui-navigation__item--active Active nav item (highlighted bg)
229+
.l-ui-navigation__item Nav item (base class, required)
230+
.l-ui-navigation__item--active Active nav item (combine with l-ui-navigation__item)
208231
.l-ui-navigation__item-icon Icon image inside a nav item
209232
.l-ui-navigation__item-icon-slot Wrapper for caller-supplied icon HTML (e.g. icon fonts)
210233
.l-ui-navigation__item-label Label span inside a nav item
211234
.l-ui-navigation__section Section group (li)
212235
.l-ui-navigation__section--has-heading Section that has a visible heading
213236
.l-ui-navigation__section--separated Section with top border separator
214-
.l-ui-navigation__section--collapsible Section with toggle button heading
237+
.l-ui-navigation__section--collapsible Marker class for collapsible sections (no engine styles; host-app/JS hook)
215238
.l-ui-navigation__section-heading Plain section label (small, uppercase, muted)
216239
.l-ui-navigation__section-toggle Collapsible section heading (item-row sized button)
217240
.l-ui-navigation__section-chevron Toggle chevron (rotates when closed)
@@ -242,11 +265,13 @@ Always combine the base block with one variant modifier (e.g. `class="l-ui-notic
242265

243266
```
244267
.l-ui-panel-container Side panel container
245-
.l-ui-panel-container.open Visible panel
268+
.l-ui-panel-container--open Visible panel (toggled by Stimulus)
246269
.l-ui-panel Panel flexbox
247270
.l-ui-panel__button Floating action button
248271
.l-ui-panel__button--dragging During drag
249272
.l-ui-panel__button--snapping Snapping to edge
273+
.l-ui-panel__button--hidden Hidden when panel is open (toggled by Stimulus)
274+
.l-ui-panel__close-button Panel close button (combine with l-ui-button l-ui-button--icon)
250275
.l-ui-panel__icon Panel button inline SVG icon
251276
.l-ui-panel__icon--light Panel button icon (light, for custom image override)
252277
.l-ui-panel__icon--dark Panel button icon (dark, for custom image override)
@@ -266,8 +291,8 @@ Always combine the base block with one variant modifier (e.g. `class="l-ui-notic
266291
.l-ui-conversation__composer-input Textarea
267292
.l-ui-conversation__separator Date separator
268293
269-
.l-ui-message Message wrapper
270-
.l-ui-message--sent Sent message (right-aligned)
294+
.l-ui-message Message wrapper (required with --sent)
295+
.l-ui-message--sent Sent message modifier (right-aligned; combine with l-ui-message)
271296
.l-ui-message__avatar User avatar
272297
.l-ui-message__bubble Message bubble
273298
.l-ui-message__author Author name
@@ -276,7 +301,7 @@ Always combine the base block with one variant modifier (e.g. `class="l-ui-notic
276301
.l-ui-message__timestamp Timestamp
277302
278303
.l-ui-stream-fade Fade-in for streamed chunks as they arrive (0.5s ease-out)
279-
.l-ui-stream-fade-word Per-word staggered fade-in for completed responses
304+
.l-ui-stream-fade__word Per-word staggered fade-in for completed responses
280305
(0.5s ease-out; set --i to the word index for a 25ms
281306
stagger, capped at 1s)
282307
```

.claude/skills/layered-ui-rails/references/HELPERS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ l_ui_title_bar(title:, breadcrumbs: [], actions: nil, &block)
8080
["Admin", admin_path]
8181
]
8282
) do %>
83-
<%= link_to "New user", new_user_path, class: "l-ui-button--primary" %>
83+
<%= link_to "New user", new_user_path, class: "l-ui-button l-ui-button--primary" %>
8484
<% end %>
8585
```
8686

@@ -132,7 +132,7 @@ Custom mode:
132132
<%= render "layered_ui/shared/search_field", form: f, field: :name_cont, label: "Name" %>
133133
<%= render "layered_ui/shared/search_select", form: f, field: :status_eq,
134134
label: "Status", options: ["Active", "Inactive"], include_blank: "Any" %>
135-
<%= f.submit "Search", class: "l-ui-button--primary" %>
135+
<%= f.submit "Search", class: "l-ui-button l-ui-button--primary" %>
136136
<% end %>
137137
```
138138

@@ -199,7 +199,7 @@ Formats a date/time value as `"%-d %b %Y, %H:%M"` (e.g. "15 Apr 2026, 10:30"). R
199199
{ attribute: :email, render: ->(r) { r.email } },
200200
{ attribute: :created_at, label: "Joined", render: ->(r) { l_ui_format_datetime(r.created_at) } },
201201
],
202-
actions: ->(r) { link_to "Edit", edit_user_path(r) },
202+
actions: ->(r) { link_to "Edit", edit_user_path(r), class: "l-ui-table__action" },
203203
caption: "Users",
204204
query: @q,
205205
turbo_frame: "users") %>

0 commit comments

Comments
 (0)