layered-ui-rails 0.1.4 → 0.2.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/CHANGELOG.md +16 -1
- data/README.md +86 -0
- data/app/assets/tailwind/layered/ui/styles.css +8 -0
- data/app/views/layouts/layered_ui/_header.html.erb +4 -4
- data/app/views/layouts/layered_ui/_panel.html.erb +2 -2
- data/app/views/layouts/layered_ui/application.html.erb +4 -3
- data/lib/layered/ui/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 25f7ed58266ba42853fdb266805b9f8daa8f69531bae482a13e5355e9868ba5e
|
|
4
|
+
data.tar.gz: 4dcd71073bf9335ef288d849e0754d3104ee750f3f049a1369537a303af39db2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 32269b207610271eee8e2fd2c6aab706ac0c51b04d325f7e670543165ec13898ff6417e4ff966bd5bf2b3e85a692d73a7489eca18d34a873d1d7b77cf2d5ac6f
|
|
7
|
+
data.tar.gz: 9db4d1270d00b1cde0b5c6f406585efb7aa6334ddcfb9a8a64f3f75d46e05c2b82e4d20bb1e8f3064fdb47b04c55ce7f423abaf0b0a306f608a129fb4c542872
|
data/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,22 @@
|
|
|
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
|
-
## [
|
|
5
|
+
## [0.2.0] - 2026-03-31
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `yield :l_ui_head` in the engine layout `<head>`, allowing host apps to inject content (e.g. dynamic theme token overrides) via `content_for`
|
|
10
|
+
- `yield :l_ui_logo_light`, `:l_ui_logo_dark` in the header, allowing host apps to inject custom logos via `content_for`
|
|
11
|
+
- `@l_ui_icon_light_url`, `@l_ui_icon_dark_url`, `@l_ui_apple_touch_icon_url`, `@l_ui_panel_icon_light_url`, `@l_ui_panel_icon_dark_url` instance variables for per-request icon overrides
|
|
12
|
+
- `pre.l-ui-surface` style for preformatted code blocks
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Panel toggle icon override now uses `@l_ui_panel_icon_light_url` and `@l_ui_panel_icon_dark_url` instance variables, replacing `content_for :l_ui_panel_icon_light` and `:l_ui_panel_icon_dark`
|
|
17
|
+
|
|
18
|
+
### Removed
|
|
19
|
+
|
|
20
|
+
- `content_for :l_ui_panel_icon_light` and `content_for :l_ui_panel_icon_dark` - use `@l_ui_panel_icon_light_url` and `@l_ui_panel_icon_dark_url` instance variables instead
|
|
6
21
|
|
|
7
22
|
## [0.1.4] - 2026-03-25
|
|
8
23
|
|
data/README.md
CHANGED
|
@@ -65,6 +65,92 @@ Then update your application layout to render the engine layout:
|
|
|
65
65
|
- **Customisable branding** - Override the default logos and icons and colors
|
|
66
66
|
- **Google Lighthouse** - `layered-ui-rails` scores a [perfect 100](https://github.com/layered-ai-public/layered-ui-rails/raw/refs/heads/main/test/dummy/app/assets/images/lighthouse.webp) across all four Google Lighthouse categories - performance, accessibility, best practices, and SEO
|
|
67
67
|
|
|
68
|
+
## Customising theme tokens
|
|
69
|
+
|
|
70
|
+
All colors are CSS custom properties on `:root`. Override any token in your stylesheet (after importing the engine CSS):
|
|
71
|
+
|
|
72
|
+
```css
|
|
73
|
+
/* app/assets/tailwind/application.css */
|
|
74
|
+
@import "./layered_ui";
|
|
75
|
+
|
|
76
|
+
:root {
|
|
77
|
+
--accent: 220 80% 55%;
|
|
78
|
+
--accent-foreground: 0 0% 100%;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.dark {
|
|
82
|
+
--accent: 220 80% 65%;
|
|
83
|
+
--accent-foreground: 0 0% 9%;
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
For dynamic theming (e.g. per-tenant branding), use `content_for :l_ui_head` to inject content into the layout `<head>`:
|
|
88
|
+
|
|
89
|
+
```erb
|
|
90
|
+
<% content_for :l_ui_head do %>
|
|
91
|
+
<style>
|
|
92
|
+
:root { --accent: <%= @tenant.accent_hsl %>; --accent-foreground: 0 0% 100%; }
|
|
93
|
+
</style>
|
|
94
|
+
<% end %>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
> **Security:** never interpolate user-supplied strings directly into a `<style>` tag - this allows CSS injection (Important: Validate or sanitise any user-derived values before interpolation).
|
|
98
|
+
|
|
99
|
+
> **CSP compatibility:** inline `<style>` blocks are blocked by a strict `Content-Security-Policy: style-src 'self'` header. If your app enforces a strict CSP, add a nonce to the style tag using Rails' `content_security_policy_nonce` helper - Rails automatically includes the matching nonce in the CSP header:
|
|
100
|
+
>
|
|
101
|
+
> ```erb
|
|
102
|
+
> <% content_for :l_ui_head do %>
|
|
103
|
+
> <style nonce="<%= content_security_policy_nonce %>">
|
|
104
|
+
> :root { --accent: <%= @tenant.accent_hsl %>; --accent-foreground: 0 0% 100%; }
|
|
105
|
+
> </style>
|
|
106
|
+
> <% end %>
|
|
107
|
+
> ```
|
|
108
|
+
|
|
109
|
+
See the [Colors documentation](https://layered-ui-rails.layered.ai/pages/layout_colors) for the full list of tokens.
|
|
110
|
+
|
|
111
|
+
## Customising logos and icons
|
|
112
|
+
|
|
113
|
+
Replace the defaults by placing files with the same names in `app/assets/images/layered_ui/` in your host app:
|
|
114
|
+
|
|
115
|
+
| File | Used for |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `logo_light.svg` | Header logo (light theme) |
|
|
118
|
+
| `logo_dark.svg` | Header logo (dark theme) |
|
|
119
|
+
| `icon_light.svg` | Favicon and header icon (light theme) |
|
|
120
|
+
| `icon_dark.svg` | Favicon and header icon (dark theme) |
|
|
121
|
+
| `apple_touch_icon.png` | Apple touch icon |
|
|
122
|
+
| `panel_icon_light.svg` | Panel toggle button (light theme) |
|
|
123
|
+
| `panel_icon_dark.svg` | Panel toggle button (dark theme) |
|
|
124
|
+
|
|
125
|
+
layered-ui-rails uses two patterns for per-request overrides:
|
|
126
|
+
|
|
127
|
+
- **Instance variables** (`@l_ui_*`) - used for small changes like URLs. The engine renders the surrounding markup and just swaps the src/href.
|
|
128
|
+
- **`content_for`** - used when the full markup needs to change. You supply the complete tag, so you can set classes, attributes, or wrap elements as needed.
|
|
129
|
+
|
|
130
|
+
For per-request logos (e.g. per-tenant branding), use `content_for` because the `<img>` tag itself carries classes that control layout and theme switching:
|
|
131
|
+
|
|
132
|
+
```erb
|
|
133
|
+
<% content_for :l_ui_logo_light do %>
|
|
134
|
+
<%= image_tag @tenant.logo_light_url, alt: "", class: "l-ui-header__logo l-ui-header__logo--light" %>
|
|
135
|
+
<% end %>
|
|
136
|
+
|
|
137
|
+
<% content_for :l_ui_logo_dark do %>
|
|
138
|
+
<%= image_tag @tenant.logo_dark_url, alt: "", class: "l-ui-header__logo l-ui-header__logo--dark" %>
|
|
139
|
+
<% end %>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
For per-request icons, set instance variables - the engine renders `<link>` and `<img>` tags that only need a URL to vary:
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
@l_ui_icon_light_url = @tenant.icon_light_url
|
|
146
|
+
@l_ui_icon_dark_url = @tenant.icon_dark_url
|
|
147
|
+
@l_ui_apple_touch_icon_url = @tenant.apple_touch_icon_url
|
|
148
|
+
@l_ui_panel_icon_light_url = @tenant.panel_icon_light_url
|
|
149
|
+
@l_ui_panel_icon_dark_url = @tenant.panel_icon_dark_url
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
> **Security:** Rails HTML-escapes URL values, so XSS via attribute injection is mitigated. However, if values are tenant-controlled, validate that they are legitimate URLs - reject `javascript:` schemes and ensure values point to expected origins.
|
|
153
|
+
|
|
68
154
|
## Documentation
|
|
69
155
|
|
|
70
156
|
An online version of the documentation is available at **[layered-ui-rails.layered.ai](https://layered-ui-rails.layered.ai)**.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<header class="l-ui-container--header" role="banner">
|
|
2
2
|
<div class="l-ui-header">
|
|
3
3
|
<%= link_to "/", "aria-label": "Home" do %>
|
|
4
|
-
<%= image_tag("layered_ui/icon_light.svg", alt: "", class: "l-ui-header__icon l-ui-header__icon--light") %>
|
|
5
|
-
<%= image_tag("layered_ui/icon_dark.svg", alt: "", class: "l-ui-header__icon l-ui-header__icon--dark") %>
|
|
6
|
-
<%= image_tag("layered_ui/logo_light.svg", alt: "", class: "l-ui-header__logo l-ui-header__logo--light") %>
|
|
7
|
-
<%= image_tag("layered_ui/logo_dark.svg", alt: "", class: "l-ui-header__logo l-ui-header__logo--dark") %>
|
|
4
|
+
<%= image_tag(@l_ui_icon_light_url.presence || "layered_ui/icon_light.svg", alt: "", class: "l-ui-header__icon l-ui-header__icon--light") %>
|
|
5
|
+
<%= image_tag(@l_ui_icon_dark_url.presence || "layered_ui/icon_dark.svg", alt: "", class: "l-ui-header__icon l-ui-header__icon--dark") %>
|
|
6
|
+
<%= yield(:l_ui_logo_light).presence || image_tag("layered_ui/logo_light.svg", alt: "", class: "l-ui-header__logo l-ui-header__logo--light") %>
|
|
7
|
+
<%= yield(:l_ui_logo_dark).presence || image_tag("layered_ui/logo_dark.svg", alt: "", class: "l-ui-header__logo l-ui-header__logo--dark") %>
|
|
8
8
|
<% end %>
|
|
9
9
|
|
|
10
10
|
<nav class="l-ui-header__navigation" aria-label="Header navigation">
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
data-action="click->l-ui--panel-button#queueToggle dblclick->l-ui--panel-button#cycleCorner mousedown->l-ui--panel-button#startDrag touchstart->l-ui--panel-button#startDrag"
|
|
66
66
|
data-l-ui--panel-target="actionButton"
|
|
67
67
|
>
|
|
68
|
-
<%= image_tag("layered_ui/panel_icon_light.svg", alt: "", class: "l-ui-icon--lg l-ui-panel__icon--light", aria: { hidden: true }) %>
|
|
69
|
-
<%= image_tag("layered_ui/panel_icon_dark.svg", alt: "", class: "l-ui-icon--lg l-ui-panel__icon--dark", aria: { hidden: true }) %>
|
|
68
|
+
<%= image_tag(@l_ui_panel_icon_light_url.presence || "layered_ui/panel_icon_light.svg", alt: "", class: "l-ui-icon--lg l-ui-panel__icon--light", aria: { hidden: true }) %>
|
|
69
|
+
<%= image_tag(@l_ui_panel_icon_dark_url.presence || "layered_ui/panel_icon_dark.svg", alt: "", class: "l-ui-icon--lg l-ui-panel__icon--dark", aria: { hidden: true }) %>
|
|
70
70
|
</button>
|
|
71
71
|
</div>
|
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
<%= csrf_meta_tags %>
|
|
15
15
|
<%= csp_meta_tag %>
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
<%= tag.link(rel: "icon", href: @l_ui_icon_light_url.presence || asset_path('layered_ui/icon_light.svg'), media: "(prefers-color-scheme: light)") %>
|
|
18
|
+
<%= tag.link(rel: "icon", href: @l_ui_icon_dark_url.presence || asset_path('layered_ui/icon_dark.svg'), media: "(prefers-color-scheme: dark)") %>
|
|
19
|
+
<%= tag.link(rel: "apple-touch-icon", href: @l_ui_apple_touch_icon_url.presence || asset_path('layered_ui/apple_touch_icon.png')) %>
|
|
20
20
|
|
|
21
21
|
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
|
|
22
|
+
<%= yield(:l_ui_head) %>
|
|
22
23
|
<%= javascript_importmap_tags %>
|
|
23
24
|
</head>
|
|
24
25
|
|
data/lib/layered/ui/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- layered.ai
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|