layered-ui-rails 0.3.0 → 0.4.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 +4 -4
- data/.claude/skills/layered-ui-rails/SKILL.md +189 -0
- data/.claude/skills/layered-ui-rails/references/CONTROLLERS.md +156 -0
- data/.claude/skills/layered-ui-rails/references/CSS.md +298 -0
- data/.claude/skills/layered-ui-rails/references/HELPERS.md +170 -0
- data/CHANGELOG.md +16 -0
- data/README.md +18 -0
- data/app/assets/tailwind/layered/ui/styles.css +34 -5
- data/app/helpers/layered/ui/breadcrumbs_helper.rb +22 -0
- data/app/helpers/layered/ui/form_helper.rb +79 -0
- data/app/helpers/layered/ui/ransack_helper.rb +27 -2
- data/app/helpers/layered/ui/table_helper.rb +104 -0
- data/app/views/layered/ui/managed_resource/_field.html.erb +22 -0
- data/app/views/layered/ui/managed_resource/_field_input.html.erb +41 -0
- data/app/views/layered/ui/managed_resource/_form.html.erb +16 -0
- data/app/views/layouts/layered_ui/application.html.erb +2 -0
- data/lib/generators/layered/ui/install_agent_skill_generator.rb +26 -0
- data/lib/layered/ui/engine.rb +3 -0
- data/lib/layered/ui/version.rb +1 -1
- metadata +12 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0596cb85b0ecaf7e9efe534a5d7b7ff9e9c3f0848d1faf8972ce7f1e4617fb1b'
|
|
4
|
+
data.tar.gz: f03e28644cd462bcf5988d851d4f85ad177761fd37eea52ed404a4046b230404
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 72a5b12ad90bd8997f03e7f9624af1ef3cfeeffc25db54a147c435bde91bb6ed49c353bf049210ec122eae10297db94b96d1918c88131acb13fac1786171feca
|
|
7
|
+
data.tar.gz: 150ccf137e7e41cb2181444b11c73cb3bb583ae3e1160b3f87cc70ee019acf34c688af70e6a09ad04b5ff756a5e8f7f72fd4e9afedbb408a052ad6b81cde41ca
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: layered-ui-rails
|
|
3
|
+
description: Installs, configures, and builds with the layered-ui-rails gem - a Rails 8+ engine providing WCAG 2.2 AA compliant layout, components, and Stimulus controllers with dark/light theming. Use when adding layered-ui-rails to a Rails app, building views with its layout and helpers, styling with its CSS classes, or troubleshooting setup.
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
compatibility: Requires Ruby on Rails >= 8.0, tailwindcss-rails >= 4.0, importmap-rails >= 2.0, stimulus-rails >= 1.0
|
|
6
|
+
metadata:
|
|
7
|
+
author: layered.ai
|
|
8
|
+
version: "1.0"
|
|
9
|
+
source: https://github.com/layered-ai-public/layered-ui-rails
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# layered-ui-rails
|
|
13
|
+
|
|
14
|
+
A Rails 8+ engine providing WCAG 2.2 AA compliant design tokens, Tailwind CSS components, and Stimulus controllers for theme switching, mobile navigation, slide-out panels, modals, and tabs.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
bundle add layered-ui-rails
|
|
20
|
+
bin/rails generate layered:ui:install
|
|
21
|
+
```
|
|
22
|
+
|
|
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
|
+
|
|
25
|
+
Then render the engine layout from your application layout:
|
|
26
|
+
|
|
27
|
+
```erb
|
|
28
|
+
<%= render template: "layouts/layered_ui/application" %>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Layout structure
|
|
32
|
+
|
|
33
|
+
The engine layout provides a fixed header (63px), optional sidebar navigation (240px wide), optional resizable panel (320px default), and a main content area. Dark mode is built in with a toggle and localStorage persistence.
|
|
34
|
+
|
|
35
|
+
### Content blocks
|
|
36
|
+
|
|
37
|
+
Populate layout regions with `content_for`:
|
|
38
|
+
|
|
39
|
+
```erb
|
|
40
|
+
<%# Navigation sidebar items %>
|
|
41
|
+
<% content_for :l_ui_navigation_items do %>
|
|
42
|
+
<%= l_ui_navigation_item("Dashboard", dashboard_path) %>
|
|
43
|
+
<%= l_ui_navigation_item("Users", users_path) %>
|
|
44
|
+
<% end %>
|
|
45
|
+
|
|
46
|
+
<%# Side panel %>
|
|
47
|
+
<% content_for :l_ui_panel_heading do %>
|
|
48
|
+
Help
|
|
49
|
+
<% end %>
|
|
50
|
+
<% content_for :l_ui_panel_body do %>
|
|
51
|
+
<p>Panel content here.</p>
|
|
52
|
+
<% end %>
|
|
53
|
+
|
|
54
|
+
<%# Inject into <head> (e.g. per-tenant theming) %>
|
|
55
|
+
<% content_for :l_ui_head do %>
|
|
56
|
+
<style nonce="<%= content_security_policy_nonce %>">
|
|
57
|
+
:root { --accent: 220 80% 55%; }
|
|
58
|
+
</style>
|
|
59
|
+
<% end %>
|
|
60
|
+
|
|
61
|
+
<%# Add CSS classes to <body> %>
|
|
62
|
+
<% content_for :l_ui_body_class do %>
|
|
63
|
+
l-ui-body--always-show-navigation
|
|
64
|
+
<% end %>
|
|
65
|
+
|
|
66
|
+
<%# Override logos %>
|
|
67
|
+
<% content_for :l_ui_logo_light do %>
|
|
68
|
+
<%= image_tag "my_logo.svg", alt: "", class: "l-ui-header__logo l-ui-header__logo--light" %>
|
|
69
|
+
<% end %>
|
|
70
|
+
<% content_for :l_ui_logo_dark do %>
|
|
71
|
+
<%= image_tag "my_logo_dark.svg", alt: "", class: "l-ui-header__logo l-ui-header__logo--dark" %>
|
|
72
|
+
<% end %>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Body class modifiers:
|
|
76
|
+
- `l-ui-body--always-show-navigation` - pins navigation as a sidebar on desktop
|
|
77
|
+
- `l-ui-body--hide-header` - hides the header and collapses its space
|
|
78
|
+
|
|
79
|
+
### Controller instance variables
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
@page_title = "Users" # Sets <title>
|
|
83
|
+
@page_description = "Manage users" # Sets <meta name="description">
|
|
84
|
+
@l_ui_icon_light_url = url # Override favicon (light)
|
|
85
|
+
@l_ui_icon_dark_url = url # Override favicon (dark)
|
|
86
|
+
@l_ui_apple_touch_icon_url = url # Override apple touch icon
|
|
87
|
+
@l_ui_panel_icon_light_url = url # Override panel button icon (light)
|
|
88
|
+
@l_ui_panel_icon_dark_url = url # Override panel button icon (dark)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## View helpers
|
|
92
|
+
|
|
93
|
+
All helpers use the `l_ui_` prefix and are available in all views automatically. See `references/HELPERS.md` for full signatures and examples.
|
|
94
|
+
|
|
95
|
+
Quick reference:
|
|
96
|
+
|
|
97
|
+
| Helper | Purpose |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `l_ui_navigation_item(label, path, active: nil, &block)` | Sidebar nav link with optional nesting |
|
|
100
|
+
| `l_ui_breadcrumbs(&block)` | Breadcrumb nav wrapper |
|
|
101
|
+
| `l_ui_breadcrumb_item(label, path = nil)` | Individual breadcrumb |
|
|
102
|
+
| `l_ui_pagy(pagy)` | Styled pagination (requires pagy gem) |
|
|
103
|
+
| `l_ui_search_form(query, url:, fields:, ...)` | Search form (requires ransack gem) |
|
|
104
|
+
| `l_ui_sort_link(query, attribute, label = nil, ...)` | Sortable table header (requires ransack gem) |
|
|
105
|
+
| `l_ui_user_signed_in?` | Check if user is authenticated |
|
|
106
|
+
| `l_ui_current_user` | Current user object |
|
|
107
|
+
|
|
108
|
+
## CSS classes
|
|
109
|
+
|
|
110
|
+
All classes use the `l-ui-` prefix with BEM naming. Use these in host app views rather than raw Tailwind utilities. See `references/CSS.md` for the full catalogue.
|
|
111
|
+
|
|
112
|
+
Key components:
|
|
113
|
+
|
|
114
|
+
| Component | Key classes |
|
|
115
|
+
|---|---|
|
|
116
|
+
| Page layout | `.l-ui-page`, `--with-navigation`, `--vertically-centered`, `--width-constrained` |
|
|
117
|
+
| Buttons | `.l-ui-button`, `--primary`, `--outline`, `--outline-danger`, `--full`, `--icon` |
|
|
118
|
+
| Surfaces | `.l-ui-surface`, `--active`, `--sm`, `--collapsible` |
|
|
119
|
+
| Forms | `.l-ui-form`, `.l-ui-form__group`, `.l-ui-form__field`, `.l-ui-label`, `.l-ui-select` |
|
|
120
|
+
| Tables | `.l-ui-table`, `.l-ui-table__header`, `.l-ui-table__cell`, `--primary`, `--action` |
|
|
121
|
+
| Badges | `.l-ui-badge`, `--rounded`, `--default`, `--success`, `--warning`, `--danger` |
|
|
122
|
+
| Notices | `.l-ui-notice--success`, `--warning`, `--error` |
|
|
123
|
+
| Tabs | `.l-ui-tabs__list`, `.l-ui-tabs__tab`, `--active` |
|
|
124
|
+
| Modal | `.l-ui-modal`, `.l-ui-modal__header`, `.l-ui-modal__body` |
|
|
125
|
+
|
|
126
|
+
## Stimulus controllers
|
|
127
|
+
|
|
128
|
+
All controllers use the `l-ui--` namespace and are auto-registered via importmap.
|
|
129
|
+
|
|
130
|
+
| Controller | Identifier | Purpose |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| Theme | `l-ui--theme` | Dark/light mode toggle with localStorage |
|
|
133
|
+
| Navigation | `l-ui--navigation` | Responsive sidebar with backdrop |
|
|
134
|
+
| Panel | `l-ui--panel` | Resizable side panel (Cmd/Ctrl+I toggle) |
|
|
135
|
+
| Panel button | `l-ui--panel-button` | Draggable floating action button |
|
|
136
|
+
| Panel resize | `l-ui--panel-resize` | Panel width drag handle |
|
|
137
|
+
| Modal | `l-ui--modal` | Native `<dialog>` with focus trap |
|
|
138
|
+
| Tabs | `l-ui--tabs` | Accessible tabbed interface |
|
|
139
|
+
| Search form | `l-ui--search-form` | Multi-scope search with Turbo support |
|
|
140
|
+
|
|
141
|
+
## Theming
|
|
142
|
+
|
|
143
|
+
Override CSS custom properties after the engine import. Values are space-separated HSL channels (no `hsl()` wrapper).
|
|
144
|
+
|
|
145
|
+
```css
|
|
146
|
+
@import "./layered_ui";
|
|
147
|
+
|
|
148
|
+
:root {
|
|
149
|
+
--accent: 220 80% 55%;
|
|
150
|
+
--accent-foreground: 0 0% 100%;
|
|
151
|
+
}
|
|
152
|
+
.dark {
|
|
153
|
+
--accent: 220 80% 65%;
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Key tokens: `--accent`, `--accent-foreground`, `--background`, `--foreground`, `--foreground-muted`, `--border`, `--border-control`, `--surface`, `--surface-active`, `--danger`, `--header-height`.
|
|
158
|
+
|
|
159
|
+
## Asset overrides
|
|
160
|
+
|
|
161
|
+
Place files in `app/assets/images/layered_ui/` to replace engine defaults:
|
|
162
|
+
|
|
163
|
+
`logo_light.svg`, `logo_dark.svg`, `icon_light.svg`, `icon_dark.svg`, `apple_touch_icon.png`, `panel_icon_light.svg`, `panel_icon_dark.svg`.
|
|
164
|
+
|
|
165
|
+
## Optional integrations
|
|
166
|
+
|
|
167
|
+
- **Devise** - auto-detected. Provides styled auth views, header login/register buttons, sidebar user info and logout.
|
|
168
|
+
- **Pagy** - auto-detected. Use `l_ui_pagy(@pagy)` for styled pagination.
|
|
169
|
+
- **Ransack** - auto-detected. Use `l_ui_search_form` and `l_ui_sort_link` for styled search and sortable tables.
|
|
170
|
+
|
|
171
|
+
## Configuration
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
# config/initializers/layered_ui.rb
|
|
175
|
+
Layered::Ui.current_user_method = :current_member # default: :current_user
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Common issues
|
|
179
|
+
|
|
180
|
+
- **Tailwind classes not generated** - The host app's Tailwind build only sees classes in the host app's templates. Use `l-ui-` classes (which are in the copied CSS) rather than raw Tailwind utilities when styling engine-provided patterns.
|
|
181
|
+
- **Missing styles** - Ensure `@import "./layered_ui";` is in `app/assets/tailwind/application.css`.
|
|
182
|
+
- **Missing JS controllers** - Ensure `import "layered_ui"` is in `app/javascript/application.js`.
|
|
183
|
+
|
|
184
|
+
## Further reference
|
|
185
|
+
|
|
186
|
+
- `references/HELPERS.md` - full helper signatures and examples
|
|
187
|
+
- `references/CSS.md` - complete CSS class catalogue
|
|
188
|
+
- `references/CONTROLLERS.md` - Stimulus controller targets, actions, and usage patterns
|
|
189
|
+
- Live demo: https://layered-ui-rails.layered.ai
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Stimulus controllers
|
|
2
|
+
|
|
3
|
+
All controllers use the `l-ui--` namespace and are auto-registered via importmap. No manual registration is needed in the host app.
|
|
4
|
+
|
|
5
|
+
## Theme (`l-ui--theme`)
|
|
6
|
+
|
|
7
|
+
Toggles dark/light mode with localStorage persistence and system preference detection.
|
|
8
|
+
|
|
9
|
+
**Targets:** `button`
|
|
10
|
+
**Actions:** `toggle`
|
|
11
|
+
**Storage key:** `theme` (stores `"dark"` or `"light"`)
|
|
12
|
+
|
|
13
|
+
The layout already wires this up. To add your own toggle button:
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<button data-controller="l-ui--theme"
|
|
17
|
+
data-action="click->l-ui--theme#toggle"
|
|
18
|
+
data-l-ui--theme-target="button"
|
|
19
|
+
aria-pressed="false">
|
|
20
|
+
Toggle theme
|
|
21
|
+
</button>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Navigation (`l-ui--navigation`)
|
|
25
|
+
|
|
26
|
+
Responsive sidebar navigation with backdrop overlay on mobile.
|
|
27
|
+
|
|
28
|
+
**Targets:** `navigation`, `backdrop`, `toggleButton`, `openIcon`, `closeIcon`
|
|
29
|
+
**Actions:** `toggle`, `close`
|
|
30
|
+
**Keyboard:** Escape to close
|
|
31
|
+
|
|
32
|
+
The layout wires this up automatically. Navigation items are populated via `content_for :l_ui_navigation_items`.
|
|
33
|
+
|
|
34
|
+
## Modal (`l-ui--modal`)
|
|
35
|
+
|
|
36
|
+
Native `<dialog>` wrapper with focus trap, scroll lock, and focus restoration.
|
|
37
|
+
|
|
38
|
+
**Targets:** `dialog`
|
|
39
|
+
**Actions:** `open`, `close`, `closeOnBackdrop`
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<div data-controller="l-ui--modal">
|
|
43
|
+
<button data-action="click->l-ui--modal#open" class="l-ui-button">
|
|
44
|
+
Open modal
|
|
45
|
+
</button>
|
|
46
|
+
<dialog data-l-ui--modal-target="dialog" class="l-ui-modal">
|
|
47
|
+
<div class="l-ui-modal__header">
|
|
48
|
+
<h2>Title</h2>
|
|
49
|
+
<button data-action="click->l-ui--modal#close" class="l-ui-button--icon">
|
|
50
|
+
Close
|
|
51
|
+
</button>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="l-ui-modal__body">
|
|
54
|
+
Content here.
|
|
55
|
+
</div>
|
|
56
|
+
</dialog>
|
|
57
|
+
</div>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Features:
|
|
61
|
+
- Focus moves to first focusable element on open
|
|
62
|
+
- Focus returns to trigger element on close
|
|
63
|
+
- Body scroll is locked while open
|
|
64
|
+
- Supports nested modals
|
|
65
|
+
|
|
66
|
+
## Tabs (`l-ui--tabs`)
|
|
67
|
+
|
|
68
|
+
Accessible tabbed interface with keyboard navigation.
|
|
69
|
+
|
|
70
|
+
**Targets:** `tab`, `panel`
|
|
71
|
+
**Actions:** `select`, `keydown`
|
|
72
|
+
**Keyboard:** Arrow Left/Right, Home, End
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<div data-controller="l-ui--tabs">
|
|
76
|
+
<div role="tablist" class="l-ui-tabs__list">
|
|
77
|
+
<button role="tab" data-l-ui--tabs-target="tab"
|
|
78
|
+
data-action="click->l-ui--tabs#select keydown->l-ui--tabs#keydown"
|
|
79
|
+
class="l-ui-tabs__tab l-ui-tabs__tab--active"
|
|
80
|
+
aria-selected="true" tabindex="0">
|
|
81
|
+
Tab 1
|
|
82
|
+
</button>
|
|
83
|
+
<button role="tab" data-l-ui--tabs-target="tab"
|
|
84
|
+
data-action="click->l-ui--tabs#select keydown->l-ui--tabs#keydown"
|
|
85
|
+
class="l-ui-tabs__tab"
|
|
86
|
+
aria-selected="false" tabindex="-1">
|
|
87
|
+
Tab 2
|
|
88
|
+
</button>
|
|
89
|
+
</div>
|
|
90
|
+
<div role="tabpanel" data-l-ui--tabs-target="panel">
|
|
91
|
+
Content 1
|
|
92
|
+
</div>
|
|
93
|
+
<div role="tabpanel" data-l-ui--tabs-target="panel" hidden>
|
|
94
|
+
Content 2
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Panel (`l-ui--panel`)
|
|
100
|
+
|
|
101
|
+
Resizable side panel. Full-width overlay on mobile, docked sidebar on desktop.
|
|
102
|
+
|
|
103
|
+
**Targets:** `container`, `hideButton`, `actionButton`
|
|
104
|
+
**Actions:** `toggle`
|
|
105
|
+
**Keyboard:** Cmd/Ctrl+I to toggle
|
|
106
|
+
**Storage key:** `panelOpen` (stores `"true"` or `"false"`)
|
|
107
|
+
|
|
108
|
+
The layout wires this up automatically. Panel content is populated via `content_for :l_ui_panel_heading` and `content_for :l_ui_panel_body`.
|
|
109
|
+
|
|
110
|
+
## Panel button (`l-ui--panel-button`)
|
|
111
|
+
|
|
112
|
+
Draggable floating action button that toggles the panel.
|
|
113
|
+
|
|
114
|
+
**Actions:** `queueToggle`, `cycleCorner`, `startDrag`, `drag`, `stopDrag`
|
|
115
|
+
**Keyboard:** Cmd/Ctrl+Alt+1/2/3/4 to move to corners
|
|
116
|
+
**Storage key:** `panelButtonPosition` (JSON with `{edge, top}`)
|
|
117
|
+
|
|
118
|
+
Features:
|
|
119
|
+
- Drag with mouse or touch
|
|
120
|
+
- Auto-snaps to nearest screen edge
|
|
121
|
+
- Double-click cycles through four corners
|
|
122
|
+
- 5px drag threshold prevents accidental clicks
|
|
123
|
+
|
|
124
|
+
## Panel resize (`l-ui--panel-resize`)
|
|
125
|
+
|
|
126
|
+
Drag handle for resizing the panel width on desktop.
|
|
127
|
+
|
|
128
|
+
**Targets:** `container`, `handle`
|
|
129
|
+
**Actions:** `startResize`, `resize`, `stopResize`, `handleKeydown`, `resetWidth`
|
|
130
|
+
**Keyboard:** Arrow Left/Right (10px), Shift+Arrow (50px), Home/End
|
|
131
|
+
**Storage key:** `panelWidth` (pixel value)
|
|
132
|
+
|
|
133
|
+
- Min width: 240px
|
|
134
|
+
- Default width: 480px
|
|
135
|
+
- Double-click handle to reset to default
|
|
136
|
+
|
|
137
|
+
## Search form (`l-ui--search-form`)
|
|
138
|
+
|
|
139
|
+
Manages multi-scope search forms with parameter preservation and Turbo frame support.
|
|
140
|
+
|
|
141
|
+
**Values:** `scope` (String, default `"q"`)
|
|
142
|
+
**Actions:** `preserve`, `clear`
|
|
143
|
+
|
|
144
|
+
```html
|
|
145
|
+
<form data-controller="l-ui--search-form"
|
|
146
|
+
data-l-ui--search-form-scope-value="q"
|
|
147
|
+
data-action="submit->l-ui--search-form#preserve"
|
|
148
|
+
data-turbo-frame="results">
|
|
149
|
+
<!-- search fields -->
|
|
150
|
+
<button type="button" data-action="click->l-ui--search-form#clear">
|
|
151
|
+
Clear
|
|
152
|
+
</button>
|
|
153
|
+
</form>
|
|
154
|
+
```
|
|
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.
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# CSS classes
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
## Page layout
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
.l-ui-page Main content wrapper with responsive padding
|
|
9
|
+
.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
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Buttons
|
|
15
|
+
|
|
16
|
+
```
|
|
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
|
|
24
|
+
.l-ui-button--navigation-toggle Mobile navigation toggle
|
|
25
|
+
.l-ui-button--panel-close Panel close button
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Surfaces
|
|
29
|
+
|
|
30
|
+
```
|
|
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
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Forms
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
.l-ui-form Form container
|
|
45
|
+
.l-ui-form__group Vertical field group with spacing
|
|
46
|
+
.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 (*)
|
|
53
|
+
|
|
54
|
+
.l-ui-label Form label
|
|
55
|
+
.l-ui-label--checkbox Checkbox label variant
|
|
56
|
+
|
|
57
|
+
.l-ui-select Select dropdown
|
|
58
|
+
.l-ui-select-wrapper Select wrapper (custom arrow)
|
|
59
|
+
|
|
60
|
+
.l-ui-search__inline Inline search form layout
|
|
61
|
+
|
|
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
|
|
67
|
+
|
|
68
|
+
.l-ui-switch Toggle switch container
|
|
69
|
+
.l-ui-switch__input Hidden checkbox input
|
|
70
|
+
.l-ui-switch__track Visual track element
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Tables
|
|
74
|
+
|
|
75
|
+
```
|
|
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
|
+
```
|
|
89
|
+
|
|
90
|
+
WCAG 2.2 AA table pattern:
|
|
91
|
+
|
|
92
|
+
```html
|
|
93
|
+
<div class="l-ui-container--table">
|
|
94
|
+
<table class="l-ui-table">
|
|
95
|
+
<caption>Users</caption>
|
|
96
|
+
<thead class="l-ui-table__header">
|
|
97
|
+
<tr>
|
|
98
|
+
<th scope="col" class="l-ui-table__header-cell">Name</th>
|
|
99
|
+
<th scope="col" class="l-ui-table__header-cell">Email</th>
|
|
100
|
+
<th scope="col" class="l-ui-table__header-cell--action">Actions</th>
|
|
101
|
+
</tr>
|
|
102
|
+
</thead>
|
|
103
|
+
<tbody class="l-ui-table__body">
|
|
104
|
+
<tr>
|
|
105
|
+
<th scope="row" class="l-ui-table__cell--primary">Alice</th>
|
|
106
|
+
<td class="l-ui-table__cell">alice@example.com</td>
|
|
107
|
+
<td class="l-ui-table__cell--action">
|
|
108
|
+
<a href="/users/1/edit">Edit</a>
|
|
109
|
+
</td>
|
|
110
|
+
</tr>
|
|
111
|
+
</tbody>
|
|
112
|
+
</table>
|
|
113
|
+
</div>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Notices
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
.l-ui-notice--success Green success message
|
|
120
|
+
.l-ui-notice--warning Yellow warning message
|
|
121
|
+
.l-ui-notice--error Red error message
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Badges
|
|
125
|
+
|
|
126
|
+
```
|
|
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
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Tabs
|
|
136
|
+
|
|
137
|
+
```
|
|
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
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Modal
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
.l-ui-modal <dialog> element
|
|
148
|
+
.l-ui-modal__header Modal header
|
|
149
|
+
.l-ui-modal__body Scrollable modal content
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Breadcrumbs
|
|
153
|
+
|
|
154
|
+
```
|
|
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
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Pagination
|
|
162
|
+
|
|
163
|
+
```
|
|
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 (...)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Navigation
|
|
172
|
+
|
|
173
|
+
```
|
|
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
|
+
```
|
|
187
|
+
|
|
188
|
+
## Header
|
|
189
|
+
|
|
190
|
+
```
|
|
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)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Panel
|
|
205
|
+
|
|
206
|
+
```
|
|
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)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Conversation
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
.l-ui-conversation Conversation wrapper
|
|
226
|
+
.l-ui-conversation__messages Scrollable messages area
|
|
227
|
+
.l-ui-conversation__composer Message input area
|
|
228
|
+
.l-ui-conversation__composer-input Textarea
|
|
229
|
+
.l-ui-conversation__separator Date separator
|
|
230
|
+
|
|
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
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Markdown
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
.l-ui-markdown Markdown content wrapper (styles h1-h6, p, code, pre, lists, tables, blockquotes, hr)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Icon sizes
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
.l-ui-icon--sm 20px (5x5)
|
|
251
|
+
.l-ui-icon--md 24px (6x6)
|
|
252
|
+
.l-ui-icon--lg 28px (7x7)
|
|
253
|
+
.l-ui-icon--xl 32px (8x8)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Utility classes
|
|
257
|
+
|
|
258
|
+
```
|
|
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)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Theming tokens
|
|
272
|
+
|
|
273
|
+
All colour values are space-separated HSL channels (e.g. `220 80% 55%`). Override after importing the engine CSS.
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
--accent Primary action colour
|
|
277
|
+
--accent-foreground Text on accent backgrounds
|
|
278
|
+
--background Page background
|
|
279
|
+
--foreground Primary text colour
|
|
280
|
+
--foreground-muted Secondary/muted text
|
|
281
|
+
--border Default border colour
|
|
282
|
+
--border-control Form control border
|
|
283
|
+
--ring Focus ring colour
|
|
284
|
+
--surface Card/surface background
|
|
285
|
+
--surface-active Active/selected surface
|
|
286
|
+
--danger Danger/error colour
|
|
287
|
+
--danger-light Light danger background
|
|
288
|
+
--danger-text Danger text colour
|
|
289
|
+
--success-bg Success background
|
|
290
|
+
--success-text Success text
|
|
291
|
+
--switch-track-checked Checked switch track
|
|
292
|
+
--warning-bg Warning background
|
|
293
|
+
--warning-text Warning text
|
|
294
|
+
--error-bg Error background
|
|
295
|
+
--error-text Error text
|
|
296
|
+
--backdrop Backdrop overlay colour
|
|
297
|
+
--header-height Header height (default 63px)
|
|
298
|
+
```
|