layered-ui-rails 0.7.0 → 0.9.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 +6 -5
- data/.claude/skills/layered-ui-rails/references/CONTROLLERS.md +1 -0
- data/.claude/skills/layered-ui-rails/references/CSS.md +11 -1
- data/.claude/skills/layered-ui-rails/references/HELPERS.md +23 -0
- data/AGENTS.md +1 -1
- data/CHANGELOG.md +22 -0
- data/README.md +9 -11
- data/app/assets/tailwind/layered/ui/styles.css +102 -70
- data/app/helpers/layered/ui/title_bar_helper.rb +52 -0
- data/app/javascript/layered_ui/controllers/l_ui/navigation_controller.js +29 -0
- data/app/javascript/layered_ui/controllers/l_ui/panel_controller.js +19 -8
- data/app/javascript/layered_ui/utilities/scroll_lock.js +29 -0
- data/app/views/layered/ui/managed_resource/_form.html.erb +1 -1
- data/config/importmap.rb +1 -0
- data/lib/generators/layered/ui/create_overrides_generator.rb +43 -42
- data/lib/layered/ui/engine.rb +1 -0
- data/lib/layered/ui/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ff66e126597c535133ec15b8fa13fd0efeecfe319b9a60d383b051ce111234e1
|
|
4
|
+
data.tar.gz: 25872e652c050f87c8752a43b8f9184ee3f98f888a7b30086efa4531c329ed1a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 22e3a324fb57843766fc6b243071b305508d88dc8ca00dd428cce23eebb7f965cab8be974fcaeef365e74ba142bff59d28f2b8b9680ff50ab3853c94d1806079
|
|
7
|
+
data.tar.gz: d0e60d24e524aad6516e2e070db67e31d7eb049d63334f36d99463d2cd2cb392ce8e863070f1952d5387096f5bc5ec06c172346ba57c3fe2b4a78ceeb6cbe4fc
|
|
@@ -61,7 +61,7 @@ Populate layout regions with `content_for` (always above the render call):
|
|
|
61
61
|
<%# Inject into <head> (e.g. per-tenant theming) %>
|
|
62
62
|
<% content_for :l_ui_head do %>
|
|
63
63
|
<style nonce="<%= content_security_policy_nonce %>">
|
|
64
|
-
:root { --accent:
|
|
64
|
+
:root { --accent: oklch(0.58 0.19 255); }
|
|
65
65
|
</style>
|
|
66
66
|
<% end %>
|
|
67
67
|
|
|
@@ -106,6 +106,7 @@ Quick reference:
|
|
|
106
106
|
| `l_ui_navigation_item(label, path, active: nil, &block)` | Sidebar nav link with optional nesting |
|
|
107
107
|
| `l_ui_breadcrumbs(&block)` | Breadcrumb nav wrapper |
|
|
108
108
|
| `l_ui_breadcrumb_item(label, path = nil)` | Individual breadcrumb |
|
|
109
|
+
| `l_ui_title_bar(title:, breadcrumbs: [], actions: nil, &block)` | Responsive page title bar with breadcrumbs and actions |
|
|
109
110
|
| `l_ui_pagy(pagy)` | Styled pagination (requires pagy gem) |
|
|
110
111
|
| `l_ui_search_form(query, url:, fields:, ...)` | Search form (requires ransack gem) |
|
|
111
112
|
| `l_ui_sort_link(query, attribute, label = nil, ...)` | Sortable table header (requires ransack gem) |
|
|
@@ -150,17 +151,17 @@ All controllers use the `l-ui--` namespace and are auto-registered via importmap
|
|
|
150
151
|
|
|
151
152
|
## Theming
|
|
152
153
|
|
|
153
|
-
Override CSS custom properties after the engine import. Values are
|
|
154
|
+
Override CSS custom properties after the engine import. Values are full CSS colors - `oklch()` is recommended for perceptually uniform mixing and consistent contrast, but `#hex`, `rgb()`, and keywords also work. A converter such as https://oklch.com/ can help translate from hex/rgb.
|
|
154
155
|
|
|
155
156
|
```css
|
|
156
157
|
@import "./layered_ui";
|
|
157
158
|
|
|
158
159
|
:root {
|
|
159
|
-
--accent:
|
|
160
|
-
--accent-foreground: 0 0
|
|
160
|
+
--accent: oklch(0.58 0.19 255);
|
|
161
|
+
--accent-foreground: oklch(1 0 0);
|
|
161
162
|
}
|
|
162
163
|
.dark {
|
|
163
|
-
--accent:
|
|
164
|
+
--accent: oklch(0.72 0.14 255);
|
|
164
165
|
}
|
|
165
166
|
```
|
|
166
167
|
|
|
@@ -28,6 +28,7 @@ Responsive sidebar navigation with backdrop overlay on mobile.
|
|
|
28
28
|
**Targets:** `navigation`, `backdrop`, `toggleButton`, `openIcon`, `closeIcon`
|
|
29
29
|
**Actions:** `toggle`, `close`
|
|
30
30
|
**Keyboard:** Escape to close
|
|
31
|
+
**Behaviour:** Locks body scroll while the mobile overlay is open
|
|
31
32
|
|
|
32
33
|
The layout wires this up automatically. Navigation items are populated via `content_for :l_ui_navigation_items`.
|
|
33
34
|
|
|
@@ -170,6 +170,15 @@ WCAG 2.2 AA table pattern:
|
|
|
170
170
|
.l-ui-breadcrumbs__link Breadcrumb link
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
+
## Title bar
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
.l-ui-title-bar Title bar wrapper used with .l-ui-container--spread
|
|
177
|
+
.l-ui-title-bar__content Breadcrumbs and title column
|
|
178
|
+
.l-ui-title-bar__title Page title
|
|
179
|
+
.l-ui-title-bar__actions Action area
|
|
180
|
+
```
|
|
181
|
+
|
|
173
182
|
## Pagination
|
|
174
183
|
|
|
175
184
|
```
|
|
@@ -275,6 +284,7 @@ WCAG 2.2 AA table pattern:
|
|
|
275
284
|
.l-ui-sr-only Visually hidden, screen reader only
|
|
276
285
|
.l-ui-skip-link Accessibility skip link
|
|
277
286
|
.l-ui-list Styled list
|
|
287
|
+
.l-ui-hr Horizontal rule with theme border and vertical spacing
|
|
278
288
|
.l-ui-container--grid 1-col mobile, 2-col desktop grid
|
|
279
289
|
.l-ui-container--spread Flex row with space-between
|
|
280
290
|
.l-ui-container--pagy Pagination wrapper
|
|
@@ -283,7 +293,7 @@ WCAG 2.2 AA table pattern:
|
|
|
283
293
|
|
|
284
294
|
## Theming tokens
|
|
285
295
|
|
|
286
|
-
All color values are
|
|
296
|
+
All color values are full CSS colors - `oklch()` is recommended for perceptually uniform mixing and consistent contrast (e.g. `oklch(0.58 0.19 255)`), but `#hex`, `rgb()`, and keywords also work. Override after importing the engine CSS. A converter such as https://oklch.com/ can help translate from hex/rgb.
|
|
287
297
|
|
|
288
298
|
Tier 1 - Accent (quick branding):
|
|
289
299
|
```
|
|
@@ -41,6 +41,29 @@ l_ui_breadcrumb_item(label, path = nil)
|
|
|
41
41
|
<% end %>
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
## Title bar
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
l_ui_title_bar(title:, breadcrumbs: [], actions: nil, &block)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- `title` (String) - page title rendered as the `<h1>`
|
|
51
|
+
- `breadcrumbs` (Array, optional) - breadcrumb items as `[label, path]` arrays or `{ label:, path: }` hashes
|
|
52
|
+
- `actions` (String|Array, optional) - HTML-safe action content; omit when using a block
|
|
53
|
+
- `&block` - optional action markup, usually buttons or links
|
|
54
|
+
|
|
55
|
+
```erb
|
|
56
|
+
<%= l_ui_title_bar(
|
|
57
|
+
title: "Users",
|
|
58
|
+
breadcrumbs: [
|
|
59
|
+
["Home", root_path],
|
|
60
|
+
["Admin", admin_path]
|
|
61
|
+
]
|
|
62
|
+
) do %>
|
|
63
|
+
<%= link_to "New user", new_user_path, class: "l-ui-button--primary" %>
|
|
64
|
+
<% end %>
|
|
65
|
+
```
|
|
66
|
+
|
|
44
67
|
## Pagination (requires pagy gem)
|
|
45
68
|
|
|
46
69
|
```ruby
|
data/AGENTS.md
CHANGED
|
@@ -6,7 +6,7 @@ Guidance for AI agents working in this repository.
|
|
|
6
6
|
|
|
7
7
|
- **Entry:** `require "layered-ui-rails"` → `lib/layered/ui.rb` → `lib/layered/ui/engine.rb`
|
|
8
8
|
- **Engine:** importmap, assets, Pagy helpers when present; helpers: `AuthenticationHelper`, `NavigationHelper`, `PagyHelper`
|
|
9
|
-
- **CSS** `app/assets/tailwind/layered/ui/styles.css`:
|
|
9
|
+
- **CSS** `app/assets/tailwind/layered/ui/styles.css`: OKLCH 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)
|
|
12
12
|
- **JS** `app/javascript/layered_ui/`: Stimulus controllers registered as `l-ui--theme`, `l-ui--navigation`, `l-ui--panel`, `l-ui--modal`, `l-ui--tabs`
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. This project follows [Semantic Versioning](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [0.9.0] - 2026-04-26
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `l_ui_title_bar` helper for page title bars with optional actions
|
|
10
|
+
- `l-ui-hr` class for styled horizontal rules
|
|
11
|
+
- `color-scheme` CSS property set to match the active theme so native form controls (date pickers, scrollbars, etc.) render with matching light/dark styling
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Navigation and panel controllers share an extracted `scroll_lock` utility module
|
|
16
|
+
- Checkbox and radio inputs use a pointer cursor
|
|
17
|
+
- Scroll-to-bottom button is centred and has a faint shadow
|
|
18
|
+
- Form submit label shortened from "Save changes" to "Save"
|
|
19
|
+
|
|
20
|
+
## [0.8.0] - 2026-04-24
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **Breaking:** Theme tokens migrated from HSL channels (`220 80% 55%`) to full `oklch()` values for perceptually uniform mixing. `@theme` now references the vars directly instead of wrapping them in `hsl()`. Existing overrides files need to be updated: replace `hue sat% lightness%` with the equivalent `oklch(L C H)` value (or any valid CSS color - `#hex`, `rgb()`, and keywords all work now).
|
|
25
|
+
- `layered:ui:create_overrides` generator and token documentation (`SKILL.md`, `references/CSS.md`, `README.md`, dummy app colour/head pages) updated to reflect the new `oklch()` format.
|
|
26
|
+
|
|
5
27
|
## [0.7.0] - 2026-04-23
|
|
6
28
|
|
|
7
29
|
### Added
|
data/README.md
CHANGED
|
@@ -104,14 +104,14 @@ All colors are CSS custom properties on `:root` using a two-tier system:
|
|
|
104
104
|
@import "./layered_ui";
|
|
105
105
|
|
|
106
106
|
:root {
|
|
107
|
-
--accent:
|
|
108
|
-
--accent-foreground: 0 0
|
|
107
|
+
--accent: oklch(0.58 0.19 255);
|
|
108
|
+
--accent-foreground: oklch(1 0 0);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
.dark {
|
|
112
|
-
--accent:
|
|
113
|
-
--accent-foreground: 0 0
|
|
114
|
-
--button-primary-text: 0 0
|
|
112
|
+
--accent: oklch(0.72 0.14 255);
|
|
113
|
+
--accent-foreground: oklch(0.2044 0 0);
|
|
114
|
+
--button-primary-text: oklch(1 0 0); /* white icons/text on colored buttons */
|
|
115
115
|
}
|
|
116
116
|
```
|
|
117
117
|
|
|
@@ -120,7 +120,7 @@ For dynamic theming (e.g. per-tenant branding), use `content_for :l_ui_head` to
|
|
|
120
120
|
```erb
|
|
121
121
|
<% content_for :l_ui_head do %>
|
|
122
122
|
<style>
|
|
123
|
-
:root { --accent: <%= @tenant.
|
|
123
|
+
:root { --accent: <%= @tenant.accent_color %>; --accent-foreground: oklch(1 0 0); }
|
|
124
124
|
</style>
|
|
125
125
|
<% end %>
|
|
126
126
|
```
|
|
@@ -132,7 +132,7 @@ For dynamic theming (e.g. per-tenant branding), use `content_for :l_ui_head` to
|
|
|
132
132
|
> ```erb
|
|
133
133
|
> <% content_for :l_ui_head do %>
|
|
134
134
|
> <style nonce="<%= content_security_policy_nonce %>">
|
|
135
|
-
> :root { --accent: <%= @tenant.
|
|
135
|
+
> :root { --accent: <%= @tenant.accent_color %>; --accent-foreground: oklch(1 0 0); }
|
|
136
136
|
> </style>
|
|
137
137
|
> <% end %>
|
|
138
138
|
> ```
|
|
@@ -213,9 +213,7 @@ kamal deploy
|
|
|
213
213
|
|
|
214
214
|
## Contributing
|
|
215
215
|
|
|
216
|
-
This project is still in its early days. We welcome issues, feedback, and ideas - they genuinely help shape the direction of the project. That said, we're holding off on accepting pull requests
|
|
217
|
-
|
|
218
|
-
- [CLA.md](CLA.md) - contributor license agreement
|
|
216
|
+
This project is still in its early days. We welcome issues, feedback, and ideas - they genuinely help shape the direction of the project. That said, we're holding off on accepting pull requests for now to stay focused on getting the foundations right. Thank you for your patience and interest. See [CLA.md](CLA.md) for the full policy.
|
|
219
217
|
|
|
220
218
|
## License
|
|
221
219
|
|
|
@@ -225,4 +223,4 @@ Copyright 2026 LAYERED AI LIMITED (UK company number: 17056830). See [NOTICE](NO
|
|
|
225
223
|
|
|
226
224
|
## Trademarks
|
|
227
225
|
|
|
228
|
-
The source code is fully open, but the layered.ai name, logo, and brand assets are trademarks of LAYERED AI LIMITED. The Apache 2.0 license does not grant rights to use the layered.ai branding. Forks and redistributions must use a distinct name. See [TRADEMARK.md](TRADEMARK.md) for the full policy.
|
|
226
|
+
The source code is fully open, but the layered.ai name, logo, and brand assets are trademarks of LAYERED AI LIMITED. The Apache 2.0 license does not grant rights to use the layered.ai branding. Forks and redistributions must use a distinct name. See [TRADEMARK.md](TRADEMARK.md) for the full policy.
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
@variant dark (&:where(.dark, .dark *));
|
|
34
34
|
|
|
35
35
|
/*
|
|
36
|
-
* Color tokens (
|
|
36
|
+
* Color tokens (full oklch() values, e.g. "oklch(0.7 0.15 240)")
|
|
37
37
|
*
|
|
38
38
|
* Tier 1 - Accent: --accent, --accent-foreground
|
|
39
39
|
* Tier 2 - Full palette: background, foreground, border, surface, etc.
|
|
@@ -47,60 +47,62 @@
|
|
|
47
47
|
|
|
48
48
|
@layer base {
|
|
49
49
|
:root {
|
|
50
|
+
color-scheme: light;
|
|
50
51
|
/* Tier 1 - Accent */
|
|
51
|
-
--accent: 0 0
|
|
52
|
-
--accent-foreground: 0 0
|
|
52
|
+
--accent: oklch(0.2044 0 0);
|
|
53
|
+
--accent-foreground: oklch(1 0 0);
|
|
53
54
|
/* Tier 2 - Full palette */
|
|
54
|
-
--background: 0 0
|
|
55
|
-
--foreground: 0 0
|
|
56
|
-
--foreground-muted: 0 0
|
|
57
|
-
--border: 0 0
|
|
58
|
-
--border-control: 0 0
|
|
59
|
-
--ring: 0 0
|
|
60
|
-
--surface: 0 0
|
|
61
|
-
--surface-highlighted: 0 0
|
|
55
|
+
--background: oklch(1 0 0);
|
|
56
|
+
--foreground: oklch(0.2484 0 0);
|
|
57
|
+
--foreground-muted: oklch(0.4089 0 0);
|
|
58
|
+
--border: oklch(0.9312 0 0);
|
|
59
|
+
--border-control: oklch(0.6409 0 0);
|
|
60
|
+
--ring: oklch(0.2484 0 0);
|
|
61
|
+
--surface: oklch(0.9696 0 0);
|
|
62
|
+
--surface-highlighted: oklch(0.9312 0 0);
|
|
62
63
|
--button-primary-bg: var(--accent);
|
|
63
64
|
--button-primary-text: var(--accent-foreground);
|
|
64
65
|
--button-primary-icon: var(--button-primary-text);
|
|
65
|
-
--danger: 0
|
|
66
|
-
--danger-light: 0
|
|
67
|
-
--danger-text: 0
|
|
68
|
-
--success-bg:
|
|
69
|
-
--success-text:
|
|
70
|
-
--switch-track-checked:
|
|
71
|
-
--warning-bg:
|
|
72
|
-
--warning-text:
|
|
73
|
-
--error-bg: 0
|
|
74
|
-
--error-text: 0
|
|
66
|
+
--danger: oklch(0.47 0.1742 27.23);
|
|
67
|
+
--danger-light: oklch(0.9663 0.0166 17.44);
|
|
68
|
+
--danger-text: oklch(0.443 0.1634 27.14);
|
|
69
|
+
--success-bg: oklch(0.8395 0.1698 152.91);
|
|
70
|
+
--success-text: oklch(0.3088 0.0763 150.76);
|
|
71
|
+
--switch-track-checked: oklch(0.6334 0.1664 149.75);
|
|
72
|
+
--warning-bg: oklch(0.8908 0.1551 94.86);
|
|
73
|
+
--warning-text: oklch(0.3625 0.0732 93.12);
|
|
74
|
+
--error-bg: oklch(0.748 0.1306 20.64);
|
|
75
|
+
--error-text: oklch(0.2248 0.0874 28.11);
|
|
75
76
|
--header-height: 63px;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
.dark {
|
|
80
|
+
color-scheme: dark;
|
|
79
81
|
/* Tier 1 - Accent */
|
|
80
|
-
--accent: 0 0
|
|
81
|
-
--accent-foreground: 0 0
|
|
82
|
+
--accent: oklch(1 0 0);
|
|
83
|
+
--accent-foreground: oklch(0.2044 0 0);
|
|
82
84
|
/* Tier 2 - Full palette */
|
|
83
|
-
--background: 0 0
|
|
84
|
-
--foreground: 0 0
|
|
85
|
-
--foreground-muted: 0 0
|
|
86
|
-
--border: 0 0
|
|
87
|
-
--border-control: 0 0
|
|
88
|
-
--ring: 0 0
|
|
89
|
-
--surface: 0 0
|
|
90
|
-
--surface-highlighted: 0 0
|
|
85
|
+
--background: oklch(0 0 0);
|
|
86
|
+
--foreground: oklch(0.9157 0 0);
|
|
87
|
+
--foreground-muted: oklch(0.7733 0 0);
|
|
88
|
+
--border: oklch(0.2801 0 0);
|
|
89
|
+
--border-control: oklch(0.5103 0 0);
|
|
90
|
+
--ring: oklch(0.9157 0 0);
|
|
91
|
+
--surface: oklch(0.193 0 0);
|
|
92
|
+
--surface-highlighted: oklch(0.2801 0 0);
|
|
91
93
|
--button-primary-bg: var(--accent);
|
|
92
94
|
--button-primary-text: var(--accent-foreground);
|
|
93
95
|
--button-primary-icon: var(--button-primary-text);
|
|
94
|
-
--danger: 0
|
|
95
|
-
--danger-light: 0
|
|
96
|
-
--danger-text: 0
|
|
97
|
-
--success-bg:
|
|
98
|
-
--success-text:
|
|
99
|
-
--switch-track-checked:
|
|
100
|
-
--warning-bg:
|
|
101
|
-
--warning-text:
|
|
102
|
-
--error-bg: 0
|
|
103
|
-
--error-text: 0
|
|
96
|
+
--danger: oklch(0.6362 0.2102 25.49);
|
|
97
|
+
--danger-light: oklch(0.2596 0.1021 28.32);
|
|
98
|
+
--danger-text: oklch(0.6607 0.1921 24.02);
|
|
99
|
+
--success-bg: oklch(0.3385 0.0852 150.45);
|
|
100
|
+
--success-text: oklch(0.8999 0.1022 155.94);
|
|
101
|
+
--switch-track-checked: oklch(0.7176 0.1902 149.6);
|
|
102
|
+
--warning-bg: oklch(0.3625 0.0732 93.12);
|
|
103
|
+
--warning-text: oklch(0.9336 0.1 95.79);
|
|
104
|
+
--error-bg: oklch(0.2248 0.0874 28.11);
|
|
105
|
+
--error-text: oklch(0.748 0.1306 20.64);
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
/* Typography */
|
|
@@ -148,7 +150,7 @@
|
|
|
148
150
|
.l-ui-body,
|
|
149
151
|
.l-ui-body * {
|
|
150
152
|
scrollbar-width: thin;
|
|
151
|
-
scrollbar-color:
|
|
153
|
+
scrollbar-color: color-mix(in oklch, var(--foreground-muted) 30%, transparent) transparent;
|
|
152
154
|
}
|
|
153
155
|
}
|
|
154
156
|
|
|
@@ -157,29 +159,29 @@
|
|
|
157
159
|
@theme {
|
|
158
160
|
--font-manrope: 'Manrope', ui-sans-serif, system-ui, sans-serif;
|
|
159
161
|
--font-inter: 'Inter', ui-sans-serif, system-ui, sans-serif;
|
|
160
|
-
--color-accent:
|
|
161
|
-
--color-accent-foreground:
|
|
162
|
-
--color-background:
|
|
163
|
-
--color-foreground:
|
|
164
|
-
--color-foreground-muted:
|
|
165
|
-
--color-border:
|
|
166
|
-
--color-border-control:
|
|
167
|
-
--color-surface:
|
|
168
|
-
--color-surface-highlighted:
|
|
169
|
-
--color-button-primary-bg:
|
|
170
|
-
--color-button-primary-text:
|
|
171
|
-
--color-button-primary-icon:
|
|
172
|
-
--color-danger:
|
|
173
|
-
--color-danger-light:
|
|
174
|
-
--color-danger-text:
|
|
175
|
-
--color-success-bg:
|
|
176
|
-
--color-success-text:
|
|
177
|
-
--color-switch-track-checked:
|
|
178
|
-
--color-warning-bg:
|
|
179
|
-
--color-warning-text:
|
|
180
|
-
--color-error-bg:
|
|
181
|
-
--color-error-text:
|
|
182
|
-
--color-ring:
|
|
162
|
+
--color-accent: var(--accent);
|
|
163
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
164
|
+
--color-background: var(--background);
|
|
165
|
+
--color-foreground: var(--foreground);
|
|
166
|
+
--color-foreground-muted: var(--foreground-muted);
|
|
167
|
+
--color-border: var(--border);
|
|
168
|
+
--color-border-control: var(--border-control);
|
|
169
|
+
--color-surface: var(--surface);
|
|
170
|
+
--color-surface-highlighted: var(--surface-highlighted);
|
|
171
|
+
--color-button-primary-bg: var(--button-primary-bg);
|
|
172
|
+
--color-button-primary-text: var(--button-primary-text);
|
|
173
|
+
--color-button-primary-icon: var(--button-primary-icon);
|
|
174
|
+
--color-danger: var(--danger);
|
|
175
|
+
--color-danger-light: var(--danger-light);
|
|
176
|
+
--color-danger-text: var(--danger-text);
|
|
177
|
+
--color-success-bg: var(--success-bg);
|
|
178
|
+
--color-success-text: var(--success-text);
|
|
179
|
+
--color-switch-track-checked: var(--switch-track-checked);
|
|
180
|
+
--color-warning-bg: var(--warning-bg);
|
|
181
|
+
--color-warning-text: var(--warning-text);
|
|
182
|
+
--color-error-bg: var(--error-bg);
|
|
183
|
+
--color-error-text: var(--error-text);
|
|
184
|
+
--color-ring: var(--ring);
|
|
183
185
|
}
|
|
184
186
|
|
|
185
187
|
/* Accessibility */
|
|
@@ -294,6 +296,13 @@
|
|
|
294
296
|
space-y-1;
|
|
295
297
|
}
|
|
296
298
|
|
|
299
|
+
/* Horizontal rule */
|
|
300
|
+
|
|
301
|
+
.l-ui-hr {
|
|
302
|
+
@apply my-4
|
|
303
|
+
border-0 border-t border-border;
|
|
304
|
+
}
|
|
305
|
+
|
|
297
306
|
/* Markdown */
|
|
298
307
|
|
|
299
308
|
.l-ui-markdown > *:first-child {
|
|
@@ -740,6 +749,26 @@
|
|
|
740
749
|
focus-ring rounded-sm;
|
|
741
750
|
}
|
|
742
751
|
|
|
752
|
+
/* Title bar */
|
|
753
|
+
|
|
754
|
+
.l-ui-title-bar {
|
|
755
|
+
@apply w-full;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
.l-ui-title-bar__content {
|
|
759
|
+
@apply min-w-0;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
.l-ui-title-bar__title {
|
|
763
|
+
@apply mt-0;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
.l-ui-title-bar__actions {
|
|
767
|
+
@apply flex flex-wrap items-center justify-end
|
|
768
|
+
gap-2
|
|
769
|
+
shrink-0;
|
|
770
|
+
}
|
|
771
|
+
|
|
743
772
|
/* Buttons */
|
|
744
773
|
|
|
745
774
|
@utility button {
|
|
@@ -1017,7 +1046,8 @@ pre.l-ui-surface {
|
|
|
1017
1046
|
@apply w-6 h-6
|
|
1018
1047
|
accent-foreground
|
|
1019
1048
|
focus-ring
|
|
1020
|
-
rounded-sm
|
|
1049
|
+
rounded-sm
|
|
1050
|
+
cursor-pointer;
|
|
1021
1051
|
}
|
|
1022
1052
|
|
|
1023
1053
|
/* Search */
|
|
@@ -1092,7 +1122,7 @@ pre.l-ui-surface {
|
|
|
1092
1122
|
w-4 h-4
|
|
1093
1123
|
pointer-events-none;
|
|
1094
1124
|
content: '';
|
|
1095
|
-
background-color:
|
|
1125
|
+
background-color: var(--foreground);
|
|
1096
1126
|
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Cpath d='M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z'/%3E%3C/svg%3E");
|
|
1097
1127
|
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Cpath d='M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z'/%3E%3C/svg%3E");
|
|
1098
1128
|
-webkit-mask-size: contain;
|
|
@@ -1124,11 +1154,12 @@ pre.l-ui-surface {
|
|
|
1124
1154
|
@apply w-6 h-6
|
|
1125
1155
|
mr-2
|
|
1126
1156
|
accent-foreground
|
|
1127
|
-
focus-ring
|
|
1157
|
+
focus-ring
|
|
1158
|
+
cursor-pointer;
|
|
1128
1159
|
}
|
|
1129
1160
|
|
|
1130
1161
|
.l-ui-radio__label {
|
|
1131
|
-
@apply text-sm;
|
|
1162
|
+
@apply text-sm cursor-pointer;
|
|
1132
1163
|
}
|
|
1133
1164
|
|
|
1134
1165
|
/* Tabs */
|
|
@@ -1659,10 +1690,11 @@ pre.l-ui-surface {
|
|
|
1659
1690
|
.l-ui-scroll-to-bottom {
|
|
1660
1691
|
@apply
|
|
1661
1692
|
sticky bottom-2 flex items-center justify-center
|
|
1662
|
-
|
|
1693
|
+
mx-auto -mt-9 h-9 w-9
|
|
1663
1694
|
rounded-full
|
|
1664
1695
|
cursor-pointer
|
|
1665
1696
|
bg-button-primary-bg text-button-primary-icon
|
|
1697
|
+
shadow-sm
|
|
1666
1698
|
focus-ring
|
|
1667
1699
|
opacity-0 pointer-events-none
|
|
1668
1700
|
transition-opacity duration-200;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Layered
|
|
2
|
+
module Ui
|
|
3
|
+
module TitleBarHelper
|
|
4
|
+
include Layered::Ui::BreadcrumbsHelper
|
|
5
|
+
|
|
6
|
+
def l_ui_title_bar(title:, breadcrumbs: [], actions: nil, &block)
|
|
7
|
+
action_content = block_given? ? capture(&block) : actions
|
|
8
|
+
|
|
9
|
+
content_tag(:header, class: "l-ui-title-bar l-ui-container--spread") do
|
|
10
|
+
safe_join([
|
|
11
|
+
content_tag(:div, class: "l-ui-title-bar__content") do
|
|
12
|
+
safe_join([
|
|
13
|
+
l_ui_title_bar_breadcrumbs(breadcrumbs),
|
|
14
|
+
content_tag(:h1, title, class: "l-ui-title-bar__title")
|
|
15
|
+
].compact)
|
|
16
|
+
end,
|
|
17
|
+
l_ui_title_bar_actions(action_content)
|
|
18
|
+
].compact)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def l_ui_title_bar_breadcrumbs(breadcrumbs)
|
|
25
|
+
return if breadcrumbs.blank?
|
|
26
|
+
|
|
27
|
+
l_ui_breadcrumbs do
|
|
28
|
+
safe_join(breadcrumbs.map { |breadcrumb| l_ui_title_bar_breadcrumb_item(breadcrumb) })
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def l_ui_title_bar_breadcrumb_item(breadcrumb)
|
|
33
|
+
case breadcrumb
|
|
34
|
+
when Hash
|
|
35
|
+
l_ui_breadcrumb_item(breadcrumb.fetch(:label), breadcrumb[:path])
|
|
36
|
+
when Array
|
|
37
|
+
l_ui_breadcrumb_item(breadcrumb[0], breadcrumb[1])
|
|
38
|
+
else
|
|
39
|
+
l_ui_breadcrumb_item(breadcrumb)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def l_ui_title_bar_actions(action_content)
|
|
44
|
+
return if action_content.blank?
|
|
45
|
+
|
|
46
|
+
content = action_content.is_a?(Array) ? safe_join(action_content) : action_content
|
|
47
|
+
|
|
48
|
+
content_tag(:div, content, class: "l-ui-title-bar__actions")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Controller } from "@hotwired/stimulus"
|
|
2
2
|
import { announce, clearAnnounceTimeout } from "layered_ui/utilities/announce"
|
|
3
3
|
import { isMobile } from "layered_ui/utilities/layout"
|
|
4
|
+
import { lockBodyScroll, unlockBodyScroll } from "layered_ui/utilities/scroll_lock"
|
|
4
5
|
|
|
5
6
|
export default class extends Controller {
|
|
6
7
|
static targets = ["navigation", "backdrop", "toggleButton", "openIcon", "closeIcon"]
|
|
@@ -8,6 +9,7 @@ export default class extends Controller {
|
|
|
8
9
|
connect() {
|
|
9
10
|
this.previousActiveElement = null
|
|
10
11
|
this.isOpen = false
|
|
12
|
+
this.isScrollLocked = false
|
|
11
13
|
this._resizeFrame = null
|
|
12
14
|
this.boundHandleResize = () => {
|
|
13
15
|
if (this._resizeFrame) return
|
|
@@ -54,6 +56,7 @@ export default class extends Controller {
|
|
|
54
56
|
this.navigationTarget.classList.add("open")
|
|
55
57
|
this.backdropTarget.classList.add("open")
|
|
56
58
|
this.setNavigationInteractivity(true)
|
|
59
|
+
this.updateScrollLock()
|
|
57
60
|
|
|
58
61
|
// Update ARIA attributes and swap icons
|
|
59
62
|
if (this.hasToggleButtonTarget) {
|
|
@@ -89,6 +92,7 @@ export default class extends Controller {
|
|
|
89
92
|
this.navigationTarget.classList.remove("open")
|
|
90
93
|
this.backdropTarget.classList.remove("open")
|
|
91
94
|
this.setNavigationInteractivity(false)
|
|
95
|
+
this.unlockScroll()
|
|
92
96
|
|
|
93
97
|
// Update ARIA attributes and swap icons
|
|
94
98
|
if (this.hasToggleButtonTarget) {
|
|
@@ -116,6 +120,7 @@ export default class extends Controller {
|
|
|
116
120
|
clearAnnounceTimeout(this)
|
|
117
121
|
cancelAnimationFrame(this._resizeFrame)
|
|
118
122
|
window.removeEventListener("resize", this.boundHandleResize)
|
|
123
|
+
this.unlockScroll()
|
|
119
124
|
this.previousActiveElement = null
|
|
120
125
|
}
|
|
121
126
|
|
|
@@ -125,6 +130,7 @@ export default class extends Controller {
|
|
|
125
130
|
// In overlay mode (default), always respect isOpen state regardless of viewport
|
|
126
131
|
if (isMobile() || !this.alwaysShow) {
|
|
127
132
|
this.setNavigationInteractivity(this.isOpen)
|
|
133
|
+
this.updateScrollLock()
|
|
128
134
|
return
|
|
129
135
|
}
|
|
130
136
|
|
|
@@ -133,6 +139,7 @@ export default class extends Controller {
|
|
|
133
139
|
this.navigationTarget.classList.remove("open")
|
|
134
140
|
this.backdropTarget.classList.remove("open")
|
|
135
141
|
this.setNavigationInteractivity(true)
|
|
142
|
+
this.unlockScroll()
|
|
136
143
|
|
|
137
144
|
if (this.hasToggleButtonTarget) {
|
|
138
145
|
this.toggleButtonTarget.setAttribute("aria-expanded", "false")
|
|
@@ -157,6 +164,28 @@ export default class extends Controller {
|
|
|
157
164
|
this.navigationTarget.removeAttribute("aria-hidden")
|
|
158
165
|
}
|
|
159
166
|
|
|
167
|
+
updateScrollLock() {
|
|
168
|
+
if (this.isOpen && isMobile()) {
|
|
169
|
+
this.lockScroll()
|
|
170
|
+
} else {
|
|
171
|
+
this.unlockScroll()
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
lockScroll() {
|
|
176
|
+
if (this.isScrollLocked) return
|
|
177
|
+
|
|
178
|
+
lockBodyScroll()
|
|
179
|
+
this.isScrollLocked = true
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
unlockScroll() {
|
|
183
|
+
if (!this.isScrollLocked) return
|
|
184
|
+
|
|
185
|
+
unlockBodyScroll()
|
|
186
|
+
this.isScrollLocked = false
|
|
187
|
+
}
|
|
188
|
+
|
|
160
189
|
get alwaysShow() {
|
|
161
190
|
return this.element.classList.contains("l-ui-body--always-show-navigation")
|
|
162
191
|
}
|
|
@@ -2,6 +2,7 @@ import { Controller } from "@hotwired/stimulus"
|
|
|
2
2
|
import { announce, clearAnnounceTimeout } from "layered_ui/utilities/announce"
|
|
3
3
|
import { storageGet, storageSet } from "layered_ui/utilities/storage"
|
|
4
4
|
import { isMobile } from "layered_ui/utilities/layout"
|
|
5
|
+
import { lockBodyScroll, unlockBodyScroll } from "layered_ui/utilities/scroll_lock"
|
|
5
6
|
|
|
6
7
|
export default class extends Controller {
|
|
7
8
|
static targets = ["container", "hideButton", "actionButton"]
|
|
@@ -9,6 +10,7 @@ export default class extends Controller {
|
|
|
9
10
|
connect() {
|
|
10
11
|
this.previousActiveElement = null
|
|
11
12
|
this.isOpen = false
|
|
13
|
+
this.isScrollLocked = false
|
|
12
14
|
this.boundKeyboardShortcut = this.handleKeyboardShortcut.bind(this)
|
|
13
15
|
this.boundCloseOnNavigate = this.closeOnMobileNavigate.bind(this)
|
|
14
16
|
const page = document.querySelector(".l-ui-page")
|
|
@@ -34,6 +36,7 @@ export default class extends Controller {
|
|
|
34
36
|
clearAnnounceTimeout(this)
|
|
35
37
|
document.removeEventListener('keydown', this.boundKeyboardShortcut)
|
|
36
38
|
document.removeEventListener('turbo:visit', this.boundCloseOnNavigate)
|
|
39
|
+
this.unlockScroll()
|
|
37
40
|
this.previousActiveElement = null
|
|
38
41
|
}
|
|
39
42
|
|
|
@@ -90,9 +93,7 @@ export default class extends Controller {
|
|
|
90
93
|
if (isMobile()) {
|
|
91
94
|
const main = document.querySelector("main")
|
|
92
95
|
if (main) main.setAttribute("inert", "")
|
|
93
|
-
this.
|
|
94
|
-
document.body.style.top = `-${this.savedScrollY}px`
|
|
95
|
-
document.body.classList.add("l-ui-scroll-lock")
|
|
96
|
+
this.lockScroll()
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
storageSet("panelOpen", "true")
|
|
@@ -124,11 +125,7 @@ export default class extends Controller {
|
|
|
124
125
|
|
|
125
126
|
const main = document.querySelector("main")
|
|
126
127
|
if (main) main.removeAttribute("inert")
|
|
127
|
-
|
|
128
|
-
document.body.style.top = ""
|
|
129
|
-
if (this.savedScrollY !== undefined) {
|
|
130
|
-
window.scrollTo(0, this.savedScrollY)
|
|
131
|
-
}
|
|
128
|
+
this.unlockScroll()
|
|
132
129
|
|
|
133
130
|
storageSet("panelOpen", "false")
|
|
134
131
|
this.updatePageMargin()
|
|
@@ -161,4 +158,18 @@ export default class extends Controller {
|
|
|
161
158
|
page.style.marginRight = ""
|
|
162
159
|
}
|
|
163
160
|
}
|
|
161
|
+
|
|
162
|
+
lockScroll() {
|
|
163
|
+
if (this.isScrollLocked) return
|
|
164
|
+
|
|
165
|
+
lockBodyScroll()
|
|
166
|
+
this.isScrollLocked = true
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
unlockScroll() {
|
|
170
|
+
if (!this.isScrollLocked) return
|
|
171
|
+
|
|
172
|
+
unlockBodyScroll()
|
|
173
|
+
this.isScrollLocked = false
|
|
174
|
+
}
|
|
164
175
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
let lockCount = 0
|
|
2
|
+
let savedScrollY = null
|
|
3
|
+
|
|
4
|
+
export function lockBodyScroll() {
|
|
5
|
+
if (lockCount === 0) {
|
|
6
|
+
savedScrollY = window.scrollY
|
|
7
|
+
document.body.style.top = `-${savedScrollY}px`
|
|
8
|
+
document.body.classList.add("l-ui-scroll-lock")
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
lockCount++
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function unlockBodyScroll() {
|
|
15
|
+
if (lockCount === 0) return
|
|
16
|
+
|
|
17
|
+
lockCount--
|
|
18
|
+
|
|
19
|
+
if (lockCount === 0) {
|
|
20
|
+
document.body.classList.remove("l-ui-scroll-lock")
|
|
21
|
+
document.body.style.top = ""
|
|
22
|
+
|
|
23
|
+
if (savedScrollY !== null) {
|
|
24
|
+
window.scrollTo(0, savedScrollY)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
savedScrollY = null
|
|
28
|
+
}
|
|
29
|
+
}
|
data/config/importmap.rb
CHANGED
|
@@ -3,6 +3,7 @@ pin "layered_ui", to: "layered_ui/index.js"
|
|
|
3
3
|
# Utilities
|
|
4
4
|
pin "layered_ui/utilities/announce", to: "layered_ui/utilities/announce.js"
|
|
5
5
|
pin "layered_ui/utilities/layout", to: "layered_ui/utilities/layout.js"
|
|
6
|
+
pin "layered_ui/utilities/scroll_lock", to: "layered_ui/utilities/scroll_lock.js"
|
|
6
7
|
pin "layered_ui/utilities/storage", to: "layered_ui/utilities/storage.js"
|
|
7
8
|
|
|
8
9
|
# Controllers
|
|
@@ -26,10 +26,11 @@ module Layered
|
|
|
26
26
|
* This file is NOT overwritten by the install generator, so your
|
|
27
27
|
* changes are preserved when you upgrade layered-ui-rails.
|
|
28
28
|
*
|
|
29
|
-
* Values are
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* Values are full `oklch()` colors (e.g. `oklch(0.7 0.15 240)`).
|
|
30
|
+
* Any valid CSS color works too - `#hex`, `rgb(...)`, keywords -
|
|
31
|
+
* but `oklch()` is recommended for perceptually uniform mixing
|
|
32
|
+
* and consistent contrast. A converter such as
|
|
33
|
+
* https://oklch.com/ can help translate from hex/rgb.
|
|
33
34
|
*/
|
|
34
35
|
|
|
35
36
|
/* ----------------------------------------------------------------
|
|
@@ -46,13 +47,13 @@ module Layered
|
|
|
46
47
|
* ---------------------------------------------------------------- */
|
|
47
48
|
|
|
48
49
|
:root {
|
|
49
|
-
/* --accent: 0 0
|
|
50
|
-
/* --accent-foreground: 0 0
|
|
50
|
+
/* --accent: oklch(0.2044 0 0); */
|
|
51
|
+
/* --accent-foreground: oklch(1 0 0); */
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
.dark {
|
|
54
|
-
/* --accent: 0 0
|
|
55
|
-
/* --accent-foreground: 0 0
|
|
55
|
+
/* --accent: oklch(1 0 0); */
|
|
56
|
+
/* --accent-foreground: oklch(0.2044 0 0); */
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
/* ----------------------------------------------------------------
|
|
@@ -63,49 +64,49 @@ module Layered
|
|
|
63
64
|
|
|
64
65
|
/*
|
|
65
66
|
:root {
|
|
66
|
-
--background: 0 0
|
|
67
|
-
--foreground: 0 0
|
|
68
|
-
--foreground-muted: 0 0
|
|
69
|
-
--border: 0 0
|
|
70
|
-
--border-control: 0 0
|
|
71
|
-
--ring: 0 0
|
|
72
|
-
--surface: 0 0
|
|
73
|
-
--surface-highlighted: 0 0
|
|
67
|
+
--background: oklch(1 0 0);
|
|
68
|
+
--foreground: oklch(0.2484 0 0);
|
|
69
|
+
--foreground-muted: oklch(0.4089 0 0);
|
|
70
|
+
--border: oklch(0.9312 0 0);
|
|
71
|
+
--border-control: oklch(0.6409 0 0);
|
|
72
|
+
--ring: oklch(0.2484 0 0);
|
|
73
|
+
--surface: oklch(0.9696 0 0);
|
|
74
|
+
--surface-highlighted: oklch(0.9312 0 0);
|
|
74
75
|
--button-primary-bg: var(--accent);
|
|
75
76
|
--button-primary-text: var(--accent-foreground);
|
|
76
77
|
--button-primary-icon: var(--button-primary-text);
|
|
77
|
-
--danger: 0
|
|
78
|
-
--danger-light: 0
|
|
79
|
-
--danger-text: 0
|
|
80
|
-
--success-bg:
|
|
81
|
-
--success-text:
|
|
82
|
-
--warning-bg:
|
|
83
|
-
--warning-text:
|
|
84
|
-
--error-bg: 0
|
|
85
|
-
--error-text: 0
|
|
78
|
+
--danger: oklch(0.47 0.1742 27.23);
|
|
79
|
+
--danger-light: oklch(0.9663 0.0166 17.44);
|
|
80
|
+
--danger-text: oklch(0.443 0.1634 27.14);
|
|
81
|
+
--success-bg: oklch(0.8395 0.1698 152.91);
|
|
82
|
+
--success-text: oklch(0.3088 0.0763 150.76);
|
|
83
|
+
--warning-bg: oklch(0.8908 0.1551 94.86);
|
|
84
|
+
--warning-text: oklch(0.3625 0.0732 93.12);
|
|
85
|
+
--error-bg: oklch(0.748 0.1306 20.64);
|
|
86
|
+
--error-text: oklch(0.2248 0.0874 28.11);
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
.dark {
|
|
89
|
-
--background: 0 0
|
|
90
|
-
--foreground: 0 0
|
|
91
|
-
--foreground-muted: 0 0
|
|
92
|
-
--border: 0 0
|
|
93
|
-
--border-control: 0 0
|
|
94
|
-
--ring: 0 0
|
|
95
|
-
--surface: 0 0
|
|
96
|
-
--surface-highlighted: 0 0
|
|
90
|
+
--background: oklch(0 0 0);
|
|
91
|
+
--foreground: oklch(0.9157 0 0);
|
|
92
|
+
--foreground-muted: oklch(0.7733 0 0);
|
|
93
|
+
--border: oklch(0.2801 0 0);
|
|
94
|
+
--border-control: oklch(0.5103 0 0);
|
|
95
|
+
--ring: oklch(0.9157 0 0);
|
|
96
|
+
--surface: oklch(0.193 0 0);
|
|
97
|
+
--surface-highlighted: oklch(0.2801 0 0);
|
|
97
98
|
--button-primary-bg: var(--accent);
|
|
98
99
|
--button-primary-text: var(--accent-foreground);
|
|
99
100
|
--button-primary-icon: var(--button-primary-text);
|
|
100
|
-
--danger: 0
|
|
101
|
-
--danger-light: 0
|
|
102
|
-
--danger-text: 0
|
|
103
|
-
--success-bg:
|
|
104
|
-
--success-text:
|
|
105
|
-
--warning-bg:
|
|
106
|
-
--warning-text:
|
|
107
|
-
--error-bg: 0
|
|
108
|
-
--error-text: 0
|
|
101
|
+
--danger: oklch(0.6362 0.2102 25.49);
|
|
102
|
+
--danger-light: oklch(0.2596 0.1021 28.32);
|
|
103
|
+
--danger-text: oklch(0.6607 0.1921 24.02);
|
|
104
|
+
--success-bg: oklch(0.3385 0.0852 150.45);
|
|
105
|
+
--success-text: oklch(0.8999 0.1022 155.94);
|
|
106
|
+
--warning-bg: oklch(0.3625 0.0732 93.12);
|
|
107
|
+
--warning-text: oklch(0.9336 0.1 95.79);
|
|
108
|
+
--error-bg: oklch(0.2248 0.0874 28.11);
|
|
109
|
+
--error-text: oklch(0.748 0.1306 20.64);
|
|
109
110
|
}
|
|
110
111
|
*/
|
|
111
112
|
|
data/lib/layered/ui/engine.rb
CHANGED
data/lib/layered/ui/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: layered-ui-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- layered.ai
|
|
@@ -215,6 +215,7 @@ files:
|
|
|
215
215
|
- app/helpers/layered/ui/pagy_helper.rb
|
|
216
216
|
- app/helpers/layered/ui/ransack_helper.rb
|
|
217
217
|
- app/helpers/layered/ui/table_helper.rb
|
|
218
|
+
- app/helpers/layered/ui/title_bar_helper.rb
|
|
218
219
|
- app/javascript/layered_ui/controllers/l_ui/modal_controller.js
|
|
219
220
|
- app/javascript/layered_ui/controllers/l_ui/navigation_controller.js
|
|
220
221
|
- app/javascript/layered_ui/controllers/l_ui/panel_button_controller.js
|
|
@@ -226,6 +227,7 @@ files:
|
|
|
226
227
|
- app/javascript/layered_ui/index.js
|
|
227
228
|
- app/javascript/layered_ui/utilities/announce.js
|
|
228
229
|
- app/javascript/layered_ui/utilities/layout.js
|
|
230
|
+
- app/javascript/layered_ui/utilities/scroll_lock.js
|
|
229
231
|
- app/javascript/layered_ui/utilities/storage.js
|
|
230
232
|
- app/views/devise/confirmations/new.html.erb
|
|
231
233
|
- app/views/devise/mailer/confirmation_instructions.html.erb
|