Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .claude/skills/layered-ui-rails/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,14 @@ Key components:

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

## Stimulus controllers
Expand Down
4 changes: 2 additions & 2 deletions .claude/skills/layered-ui-rails/references/CONTROLLERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Native `<dialog>` wrapper with focus trap, scroll lock, and focus restoration.
<dialog data-l-ui--modal-target="dialog" class="l-ui-modal">
<div class="l-ui-modal__header">
<h2>Title</h2>
<button data-action="click->l-ui--modal#close" class="l-ui-button--icon">
<button data-action="click->l-ui--modal#close" class="l-ui-button l-ui-button--icon">
Close
</button>
</div>
Expand All @@ -82,7 +82,7 @@ Accessible tabbed interface with keyboard navigation.
**Keyboard:** Arrow Left/Right, Home, End

```html
<div data-controller="l-ui--tabs">
<div class="l-ui-tabs" data-controller="l-ui--tabs">
<div role="tablist" class="l-ui-tabs__list">
<button role="tab" data-l-ui--tabs-target="tab"
data-action="click->l-ui--tabs#select keydown->l-ui--tabs#keydown"
Expand Down
103 changes: 64 additions & 39 deletions .claude/skills/layered-ui-rails/references/CSS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,49 @@

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.

## Naming conventions

Two patterns deviate from strict BEM by design - leave them in place rather than "fixing" them into elements:

**`*-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.

**Bare-link styling.** `.l-ui-page` and `.l-ui-panel__body` style descendant `<a>` elements that do not already carry an `l-ui-` class:

```css
.l-ui-page a:not([class*="l-ui-"]),
.l-ui-panel__body a:not([class*="l-ui-"]) { ... }
```

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.

## Page layout

```
.l-ui-page Main content wrapper with responsive padding
.l-ui-page--with-navigation Left margin for sidebar on desktop
.l-ui-page--vertically-centered Centred layout (e.g. login pages)
.l-ui-page--width-constrained Max-width container
.l-ui-page__vertically-centered Centred layout element (e.g. login pages)
.l-ui-page__width-constrained Max-width container element
```

## Buttons

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

```
.l-ui-button Plain button with padding and focus ring
.l-ui-button--primary Accent-coloured solid button
.l-ui-button--danger Solid red button (for destructive actions)
.l-ui-button--outline Bordered button
.l-ui-button--outline-danger Red bordered button (for destructive actions)
.l-ui-button--icon Icon-only button (fixed size, no text)
.l-ui-button--navigation-toggle Mobile navigation toggle
.l-ui-button--panel-close Panel close button
.l-ui-button Base class (required with the modifiers below)
.l-ui-button--primary Accent-coloured solid button
.l-ui-button--danger Solid red button (for destructive actions)
.l-ui-button--outline Bordered button
.l-ui-button--outline-danger Red bordered button (for destructive actions)
.l-ui-button--full Full-width (e.g. l-ui-button l-ui-button--primary l-ui-button--full)
.l-ui-button--small Compact 32px-tall variant
```

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

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

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

## Surfaces

Always combine the `l-ui-surface` base class with any modifiers (e.g. `l-ui-surface l-ui-surface--highlighted`):

```
.l-ui-surface Rounded, padded container
.l-ui-surface Base class (required with the modifiers below)
.l-ui-surface--highlighted Darker background variant
.l-ui-surface--sm Smaller padding
.l-ui-surface--collapsible Wraps a <details> element
.l-ui-surface--collapsible-highlighted Highlighted variant
.l-ui-surface--collapsible-highlighted Highlighted collapsible variant
.l-ui-surface__summary Collapsible toggle (on <summary>)
.l-ui-surface__chevron Chevron indicator (rotates on open)
.l-ui-surface__content Collapsible content area
Expand All @@ -55,7 +71,7 @@ For destructive actions use `l-ui-button--danger` (solid) or `l-ui-button--outli
```
.l-ui-form Form container
.l-ui-form__group Vertical field group with spacing
.l-ui-form__group--large-gap Larger spacing variant
.l-ui-form__group--large-gap Larger spacing modifier (combine with l-ui-form__group)
.l-ui-form__field Input/textarea styling
.l-ui-form__errors Error summary box
.l-ui-form__errors-list Bulleted error list
Expand All @@ -65,14 +81,16 @@ For destructive actions use `l-ui-button--danger` (solid) or `l-ui-button--outli
.l-ui-form__required Required indicator (*)

.l-ui-label Form label
.l-ui-label--checkbox Checkbox label variant

.l-ui-checkbox-container Checkbox row (input + label)
.l-ui-checkbox Checkbox input
.l-ui-checkbox-container__label Checkbox label element

.l-ui-select Select dropdown
.l-ui-select-wrapper Select wrapper (custom arrow)
.l-ui-select-container Select wrapper (custom arrow)

.l-ui-search-inline Inline search form layout

.l-ui-checkbox-container Checkbox container
.l-ui-radio Radio button group
.l-ui-radio__item Radio item wrapper
.l-ui-radio__input Radio input element
Expand All @@ -90,13 +108,15 @@ For destructive actions use `l-ui-button--danger` (solid) or `l-ui-button--outli
.l-ui-table__header <thead> row
.l-ui-table__header-cell <th> cell
.l-ui-table__header-cell--action Right-aligned action header
.l-ui-table__header-cell--sortable Marker class for sortable headers (no engine styles; host-app/JS hook)
.l-ui-table__sort-link Sortable header link
.l-ui-table__sort-indicator Sort direction indicator (arrow)
.l-ui-table__body <tbody>
.l-ui-table__cell Regular <td> cell
.l-ui-table__cell--primary Bold cell (typically first column, use <th scope="row">)
.l-ui-table__cell--action Right-aligned action cell
.l-ui-table__action--danger Danger action link
.l-ui-table__action Action link/button (inside a __cell--action)
.l-ui-table__action--danger Danger modifier (combine with l-ui-table__action)
.l-ui-table-container Overflow wrapper for responsive tables
```

Expand All @@ -110,14 +130,14 @@ WCAG 2.2 AA table pattern:
<tr>
<th scope="col" class="l-ui-table__header-cell">Name</th>
<th scope="col" class="l-ui-table__header-cell">Email</th>
<th scope="col" class="l-ui-table__header-cell--action">Actions</th>
<th scope="col" class="l-ui-table__header-cell l-ui-table__header-cell--action">Actions</th>
</tr>
</thead>
<tbody class="l-ui-table__body">
<tr>
<th scope="row" class="l-ui-table__cell--primary">Alice</th>
<th scope="row" class="l-ui-table__cell l-ui-table__cell--primary">Alice</th>
<td class="l-ui-table__cell">alice@example.com</td>
<td class="l-ui-table__cell--action">
<td class="l-ui-table__cell l-ui-table__cell--action">
<a href="/users/1/edit">Edit</a>
</td>
</tr>
Expand All @@ -140,20 +160,23 @@ Always combine the base block with one variant modifier (e.g. `class="l-ui-notic
## Badges

```
.l-ui-badge Base badge
.l-ui-badge Base badge (required alongside a colour modifier)
.l-ui-badge--rounded Pill shape
.l-ui-badge--default Grey
.l-ui-badge--success Green
.l-ui-badge--warning Yellow
.l-ui-badge--danger Red
```

Always combine the base block with a modifier, e.g. `<span class="l-ui-badge l-ui-badge--success">`.

## Tabs

```
.l-ui-tabs Tabs wrapper (data-controller="l-ui--tabs")
.l-ui-tabs__list Tab list container (role="tablist")
.l-ui-tabs__tab Tab button
.l-ui-tabs__tab--active Active tab with accent border
.l-ui-tabs__tab Tab button (base class, required)
.l-ui-tabs__tab--active Active tab with accent border (combine with l-ui-tabs__tab)
.l-ui-tabs__panel Tab panel content
```

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

```
.l-ui-navigation-container Sidebar container
.l-ui-navigation-container.open Visible sidebar
.l-ui-backdrop Base backdrop (always pair with a variant)
.l-ui-backdrop--navigation Navigation overlay (use with .l-ui-backdrop)
.l-ui-backdrop.open Visible backdrop
.l-ui-navigation-container Sidebar container
.l-ui-navigation-container--open Visible sidebar (toggled by Stimulus)
.l-ui-backdrop Base backdrop (always pair with a variant)
.l-ui-backdrop--navigation Navigation overlay (use with .l-ui-backdrop)
.l-ui-backdrop--open Visible backdrop (toggled by Stimulus)
.l-ui-navigation Nav flexbox
.l-ui-navigation__links Nav links list
.l-ui-navigation__item Nav item
.l-ui-navigation__item--active Active nav item (highlighted bg)
.l-ui-navigation__item Nav item (base class, required)
.l-ui-navigation__item--active Active nav item (combine with l-ui-navigation__item)
.l-ui-navigation__item-icon Icon image inside a nav item
.l-ui-navigation__item-icon-slot Wrapper for caller-supplied icon HTML (e.g. icon fonts)
.l-ui-navigation__item-label Label span inside a nav item
.l-ui-navigation__section Section group (li)
.l-ui-navigation__section--has-heading Section that has a visible heading
.l-ui-navigation__section--separated Section with top border separator
.l-ui-navigation__section--collapsible Section with toggle button heading
.l-ui-navigation__section--collapsible Marker class for collapsible sections (no engine styles; host-app/JS hook)
.l-ui-navigation__section-heading Plain section label (small, uppercase, muted)
.l-ui-navigation__section-toggle Collapsible section heading (item-row sized button)
.l-ui-navigation__section-chevron Toggle chevron (rotates when closed)
Expand Down Expand Up @@ -242,11 +265,13 @@ Always combine the base block with one variant modifier (e.g. `class="l-ui-notic

```
.l-ui-panel-container Side panel container
.l-ui-panel-container.open Visible panel
.l-ui-panel-container--open Visible panel (toggled by Stimulus)
.l-ui-panel Panel flexbox
.l-ui-panel__button Floating action button
.l-ui-panel__button--dragging During drag
.l-ui-panel__button--snapping Snapping to edge
.l-ui-panel__button--hidden Hidden when panel is open (toggled by Stimulus)
.l-ui-panel__close-button Panel close button (combine with l-ui-button l-ui-button--icon)
.l-ui-panel__icon Panel button inline SVG icon
.l-ui-panel__icon--light Panel button icon (light, for custom image override)
.l-ui-panel__icon--dark Panel button icon (dark, for custom image override)
Expand All @@ -266,8 +291,8 @@ Always combine the base block with one variant modifier (e.g. `class="l-ui-notic
.l-ui-conversation__composer-input Textarea
.l-ui-conversation__separator Date separator

.l-ui-message Message wrapper
.l-ui-message--sent Sent message (right-aligned)
.l-ui-message Message wrapper (required with --sent)
.l-ui-message--sent Sent message modifier (right-aligned; combine with l-ui-message)
.l-ui-message__avatar User avatar
.l-ui-message__bubble Message bubble
.l-ui-message__author Author name
Expand All @@ -276,7 +301,7 @@ Always combine the base block with one variant modifier (e.g. `class="l-ui-notic
.l-ui-message__timestamp Timestamp

.l-ui-stream-fade Fade-in for streamed chunks as they arrive (0.5s ease-out)
.l-ui-stream-fade-word Per-word staggered fade-in for completed responses
.l-ui-stream-fade__word Per-word staggered fade-in for completed responses
(0.5s ease-out; set --i to the word index for a 25ms
stagger, capped at 1s)
```
Expand Down
6 changes: 3 additions & 3 deletions .claude/skills/layered-ui-rails/references/HELPERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ l_ui_title_bar(title:, breadcrumbs: [], actions: nil, &block)
["Admin", admin_path]
]
) do %>
<%= link_to "New user", new_user_path, class: "l-ui-button--primary" %>
<%= link_to "New user", new_user_path, class: "l-ui-button l-ui-button--primary" %>
<% end %>
```

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

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