layered-ui-rails 0.3.0 → 0.5.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: a8e4d9560a90e896fb66753e342a555c9ce281764e2c3c7ac9d326a4a52d5ff4
4
- data.tar.gz: 3c1f55587df50d2f0def13536b0adb3c8869682246932fd73ddcd146a63a4e38
3
+ metadata.gz: 0f1a66524b7aad7bb6cca544c4936735e286f2a1a2bccf620a47bd08c3c6b3ef
4
+ data.tar.gz: 779bb09a82b63891245d917c25c40f82e2e1c505291c3967daf0f1db4a4cbb94
5
5
  SHA512:
6
- metadata.gz: 99a8dff88d32df2645427d4802d64294744cfc51d185e3b7df7b1fbf25db5887472b654293c6ce3af78440be2730a809c73f37bf282e6166d47b39ec80e85530
7
- data.tar.gz: 13f9cc999716982874cfc3a6eaff48e63a5c7a06b608e321692de90d573c74e2879229dec97bd6250f3afb5b4bdfd222fef6150643b92e7f38d77b2b28fb85f3
6
+ metadata.gz: cbac626bf832dc7599b37a4234aac65e1f8cbd1282d1c2bda20e9df3c97319ed468adde98af047affa463895d0bdcf433bd6512af4ceef59c843eba21caf443d
7
+ data.tar.gz: f62ab50c0825ee0b152fd48b1c508750cbe04bff880217aa0a7ffa4663d54db3ab285d08546fcdc52117cac2817c2e66196d3f8b96bfff4a6f584c0671e52508
@@ -0,0 +1,192 @@
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_table(records, columns:, caption:, ...)` | Styled accessible data table with optional sort and actions |
106
+ | `l_ui_form(record, fields:, url:, method:)` | Complete form with fields, error summary, and submit |
107
+ | `l_ui_normalise_field(record, config)` | Normalise a raw field config hash into canonical form |
108
+ | `l_ui_user_signed_in?` | Check if user is authenticated |
109
+ | `l_ui_current_user` | Current user object |
110
+
111
+ ## CSS classes
112
+
113
+ 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.
114
+
115
+ Key components:
116
+
117
+ | Component | Key classes |
118
+ |---|---|
119
+ | Page layout | `.l-ui-page`, `--with-navigation`, `--vertically-centered`, `--width-constrained` |
120
+ | Buttons | `.l-ui-button`, `--primary`, `--outline`, `--outline-danger`, `--full`, `--icon` |
121
+ | Surfaces | `.l-ui-surface`, `--active`, `--sm`, `--collapsible` |
122
+ | Forms | `.l-ui-form`, `.l-ui-form__group`, `.l-ui-form__field`, `.l-ui-label`, `.l-ui-select` |
123
+ | Tables | `.l-ui-table`, `.l-ui-table__header`, `.l-ui-table__cell`, `--primary`, `--action` |
124
+ | Badges | `.l-ui-badge`, `--rounded`, `--default`, `--success`, `--warning`, `--danger` |
125
+ | Notices | `.l-ui-notice--success`, `--warning`, `--error` |
126
+ | Tabs | `.l-ui-tabs__list`, `.l-ui-tabs__tab`, `--active` |
127
+ | Modal | `.l-ui-modal`, `.l-ui-modal__header`, `.l-ui-modal__body` |
128
+
129
+ ## Stimulus controllers
130
+
131
+ All controllers use the `l-ui--` namespace and are auto-registered via importmap.
132
+
133
+ | Controller | Identifier | Purpose |
134
+ |---|---|---|
135
+ | Theme | `l-ui--theme` | Dark/light mode toggle with localStorage |
136
+ | Navigation | `l-ui--navigation` | Responsive sidebar with backdrop |
137
+ | Panel | `l-ui--panel` | Resizable side panel (Cmd/Ctrl+I toggle) |
138
+ | Panel button | `l-ui--panel-button` | Draggable floating action button |
139
+ | Panel resize | `l-ui--panel-resize` | Panel width drag handle |
140
+ | Modal | `l-ui--modal` | Native `<dialog>` with focus trap |
141
+ | Tabs | `l-ui--tabs` | Accessible tabbed interface |
142
+ | Search form | `l-ui--search-form` | Multi-scope search with Turbo support |
143
+
144
+ ## Theming
145
+
146
+ Override CSS custom properties after the engine import. Values are space-separated HSL channels (no `hsl()` wrapper).
147
+
148
+ ```css
149
+ @import "./layered_ui";
150
+
151
+ :root {
152
+ --accent: 220 80% 55%;
153
+ --accent-foreground: 0 0% 100%;
154
+ }
155
+ .dark {
156
+ --accent: 220 80% 65%;
157
+ }
158
+ ```
159
+
160
+ Key tokens: `--accent`, `--accent-foreground`, `--background`, `--foreground`, `--foreground-muted`, `--border`, `--border-control`, `--surface`, `--surface-active`, `--danger`, `--header-height`.
161
+
162
+ ## Asset overrides
163
+
164
+ Place files in `app/assets/images/layered_ui/` to replace engine defaults:
165
+
166
+ `logo_light.svg`, `logo_dark.svg`, `icon_light.svg`, `icon_dark.svg`, `apple_touch_icon.png`, `panel_icon_light.svg`, `panel_icon_dark.svg`.
167
+
168
+ ## Optional integrations
169
+
170
+ - **Devise** - auto-detected. Provides styled auth views, header login/register buttons, sidebar user info and logout.
171
+ - **Pagy** - auto-detected. Use `l_ui_pagy(@pagy)` for styled pagination.
172
+ - **Ransack** - auto-detected. Use `l_ui_search_form` and `l_ui_sort_link` for styled search and sortable tables.
173
+
174
+ ## Configuration
175
+
176
+ ```ruby
177
+ # config/initializers/layered_ui.rb
178
+ Layered::Ui.current_user_method = :current_member # default: :current_user
179
+ ```
180
+
181
+ ## Common issues
182
+
183
+ - **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.
184
+ - **Missing styles** - Ensure `@import "./layered_ui";` is in `app/assets/tailwind/application.css`.
185
+ - **Missing JS controllers** - Ensure `import "layered_ui"` is in `app/javascript/application.js`.
186
+
187
+ ## Further reference
188
+
189
+ - `references/HELPERS.md` - full helper signatures and examples
190
+ - `references/CSS.md` - complete CSS class catalogue
191
+ - `references/CONTROLLERS.md` - Stimulus controller targets, actions, and usage patterns
192
+ - 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,310 @@
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
+ Standalone variants (use one of these, not combined with each other):
17
+
18
+ ```
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
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)
33
+ ```
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
+
39
+ ## Surfaces
40
+
41
+ ```
42
+ .l-ui-surface Rounded, padded container
43
+ .l-ui-surface--active Darker background variant
44
+ .l-ui-surface--sm Smaller padding
45
+ .l-ui-surface--collapsible Wraps a <details> element
46
+ .l-ui-surface--collapsible-active Open state
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
50
+ ```
51
+
52
+ ## Forms
53
+
54
+ ```
55
+ .l-ui-form Form container
56
+ .l-ui-form__group Vertical field group with spacing
57
+ .l-ui-form__group--large-gap Larger spacing variant
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 (*)
65
+
66
+ .l-ui-label Form label
67
+ .l-ui-label--checkbox Checkbox label variant
68
+
69
+ .l-ui-select Select dropdown
70
+ .l-ui-select-wrapper Select wrapper (custom arrow)
71
+
72
+ .l-ui-search__inline Inline search form layout
73
+
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
79
+
80
+ .l-ui-switch Toggle switch container
81
+ .l-ui-switch__input Hidden checkbox input
82
+ .l-ui-switch__track Visual track element
83
+ ```
84
+
85
+ ## Tables
86
+
87
+ ```
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
100
+ ```
101
+
102
+ WCAG 2.2 AA table pattern:
103
+
104
+ ```html
105
+ <div class="l-ui-container--table">
106
+ <table class="l-ui-table">
107
+ <caption>Users</caption>
108
+ <thead class="l-ui-table__header">
109
+ <tr>
110
+ <th scope="col" class="l-ui-table__header-cell">Name</th>
111
+ <th scope="col" class="l-ui-table__header-cell">Email</th>
112
+ <th scope="col" class="l-ui-table__header-cell--action">Actions</th>
113
+ </tr>
114
+ </thead>
115
+ <tbody class="l-ui-table__body">
116
+ <tr>
117
+ <th scope="row" class="l-ui-table__cell--primary">Alice</th>
118
+ <td class="l-ui-table__cell">alice@example.com</td>
119
+ <td class="l-ui-table__cell--action">
120
+ <a href="/users/1/edit">Edit</a>
121
+ </td>
122
+ </tr>
123
+ </tbody>
124
+ </table>
125
+ </div>
126
+ ```
127
+
128
+ ## Notices
129
+
130
+ ```
131
+ .l-ui-notice--success Green success message
132
+ .l-ui-notice--warning Yellow warning message
133
+ .l-ui-notice--error Red error message
134
+ ```
135
+
136
+ ## Badges
137
+
138
+ ```
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
145
+ ```
146
+
147
+ ## Tabs
148
+
149
+ ```
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
154
+ ```
155
+
156
+ ## Modal
157
+
158
+ ```
159
+ .l-ui-modal <dialog> element
160
+ .l-ui-modal__header Modal header
161
+ .l-ui-modal__body Scrollable modal content
162
+ ```
163
+
164
+ ## Breadcrumbs
165
+
166
+ ```
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
171
+ ```
172
+
173
+ ## Pagination
174
+
175
+ ```
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 (...)
181
+ ```
182
+
183
+ ## Navigation
184
+
185
+ ```
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
198
+ ```
199
+
200
+ ## Header
201
+
202
+ ```
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)
214
+ ```
215
+
216
+ ## Panel
217
+
218
+ ```
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)
232
+ ```
233
+
234
+ ## Conversation
235
+
236
+ ```
237
+ .l-ui-conversation Conversation wrapper
238
+ .l-ui-conversation__messages Scrollable messages area
239
+ .l-ui-conversation__composer Message input area
240
+ .l-ui-conversation__composer-input Textarea
241
+ .l-ui-conversation__separator Date separator
242
+
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
251
+ ```
252
+
253
+ ## Markdown
254
+
255
+ ```
256
+ .l-ui-markdown Markdown content wrapper (styles h1-h6, p, code, pre, lists, tables, blockquotes, hr)
257
+ ```
258
+
259
+ ## Icon sizes
260
+
261
+ ```
262
+ .l-ui-icon--sm 20px (5x5)
263
+ .l-ui-icon--md 24px (6x6)
264
+ .l-ui-icon--lg 28px (7x7)
265
+ .l-ui-icon--xl 32px (8x8)
266
+ ```
267
+
268
+ ## Utility classes
269
+
270
+ ```
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)
281
+ ```
282
+
283
+ ## Theming tokens
284
+
285
+ All colour values are space-separated HSL channels (e.g. `220 80% 55%`). Override after importing the engine CSS.
286
+
287
+ ```
288
+ --accent Primary action colour
289
+ --accent-foreground Text on accent backgrounds
290
+ --background Page background
291
+ --foreground Primary text colour
292
+ --foreground-muted Secondary/muted text
293
+ --border Default border colour
294
+ --border-control Form control border
295
+ --ring Focus ring colour
296
+ --surface Card/surface background
297
+ --surface-active Active/selected surface
298
+ --danger Danger/error colour
299
+ --danger-light Light danger background
300
+ --danger-text Danger text colour
301
+ --success-bg Success background
302
+ --success-text Success text
303
+ --switch-track-checked Checked switch track
304
+ --warning-bg Warning background
305
+ --warning-text Warning text
306
+ --error-bg Error background
307
+ --error-text Error text
308
+ --backdrop Backdrop overlay colour
309
+ --header-height Header height (default 63px)
310
+ ```