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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .claude/skills/layered-ui-rails/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ bundle add layered-ui-rails
bin/rails generate layered:ui:install
```

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`.
The generator adds `@import "../builds/tailwind/layered_ui";` to `application.css` (the engine's CSS is served straight from the gem via tailwindcss-rails' engine support), creates a `layered_ui_overrides.css` file for theme customisations, and adds the JS import to `application.js`.

Then render the engine layout from your application layout. Place all `content_for` blocks **above** the render call - the engine layout reads them when it renders, so they must be defined first:

Expand Down Expand Up @@ -181,7 +181,7 @@ All controllers use the `l-ui--` namespace and are auto-registered via importmap
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.

```css
@import "./layered_ui";
@import "../builds/tailwind/layered_ui";

:root {
--accent: oklch(0.58 0.19 255);
Expand Down Expand Up @@ -217,8 +217,8 @@ Layered::Ui.current_user_method = :current_member # default: :current_user

## Common issues

- **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.
- **Missing styles** - Ensure `@import "./layered_ui";` is in `app/assets/tailwind/application.css`.
- **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 defined in the engine CSS) rather than raw Tailwind utilities when styling engine-provided patterns.
- **Missing styles** - Ensure `@import "../builds/tailwind/layered_ui";` is in `app/assets/tailwind/application.css`. The import resolves to a file generated by tailwindcss-rails' engine support; it is created automatically by `tailwindcss:build`/`watch` (and `assets:precompile`), so run a build (e.g. `bin/dev`) if the file is missing.
- **Missing JS controllers** - Ensure `import "layered_ui"` is in `app/javascript/application.js`.

## Further reference
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ Guidance for AI agents working in this repository.

- **Entry:** `require "layered-ui-rails"` → `lib/layered/ui.rb` → `lib/layered/ui/engine.rb`
- **Engine:** importmap, assets, Pagy helpers when present; helpers: `AuthenticationHelper`, `NavigationHelper`, `PagyHelper`
- **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, 256px sidebar, 320px panel. WCAG 2.2 AA.
- **CSS** `app/assets/tailwind/layered_ui/engine.css` (the tailwindcss-rails engine entry point): OKLCH tokens, `.dark` on `<html>`, `@theme` utilities (`bg-background`, etc.), BEM components (`.l-ui-button--primary`, etc.). Layout: 63px header, 256px sidebar, 320px panel. WCAG 2.2 AA.
- **CSS `@apply`:** Multi-line with grouping, following the Prettier Tailwind plugin order: layout → sizing → spacing → typography → backgrounds → borders → effects → transitions → interactivity. Within each group, follow Tailwind's own ordering (not alphabetical). State variants (`hover:`, `focus:`, `active:`, `disabled:`) and responsive prefixes (`sm:`, `md:`, `lg:`) are grouped with their base utility. Single utilities may stay on one line.
- **Generators:** `bin/rails generate layered:ui:install` (copy CSS, import CSS, import JS)
- **Generators:** `bin/rails generate layered:ui:install` (import engine CSS via `@import "../builds/tailwind/layered_ui"`, create overrides file, import JS)
- **JS** `app/javascript/layered_ui/`: Stimulus controllers registered as `l-ui--theme`, `l-ui--navigation`, `l-ui--panel`, `l-ui--modal`, `l-ui--tabs`
- **Layout yields** (prefixed `l_ui_`): `:l_ui_navigation_items`, `:l_ui_panel_heading`, `:l_ui_panel_body`, `:l_ui_body_class`
- `:l_ui_body_class` modifiers: `l-ui-body--always-show-navigation` (pins nav as sidebar on desktop), `l-ui-body--hide-header` (hides header and collapses its space)
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

All notable changes to this project will be documented in this file. This project follows [Semantic Versioning](https://semver.org/).

## [Unreleased]

### Changed

- **Breaking (install flow):** the engine's CSS is now served directly from the gem using [tailwindcss-rails' engine support](https://github.com/rails/tailwindcss-rails#rails-engines-support-experimental) instead of being copied into the host app. The install generator no longer creates `app/assets/tailwind/layered_ui.css`; instead it adds `@import "../builds/tailwind/layered_ui";` to your `application.css`. The CSS now stays in sync with the installed gem version automatically - no need to re-run the generator after upgrading.
- The engine layout now links the compiled Tailwind build explicitly (`stylesheet_link_tag "tailwind"`) rather than the `:app` bundle, matching tailwindcss-rails' own convention and avoiding a stray link to the engine's intermediate build file.
- Moved the engine's source stylesheet from `app/assets/tailwind/layered/ui/styles.css` to `app/assets/tailwind/layered_ui/engine.css` (the path tailwindcss-rails' engine support expects).

### Removed

- `CopyAssetsGenerator` (`layered:ui:copy_assets`), which copied the engine CSS into the host app. It is replaced by the engine-support import above.

### Migration

Re-run `bin/rails generate layered:ui:install` (it adds the new import without duplicating existing ones), then delete the now-unused copied file at `app/assets/tailwind/layered_ui.css` and remove its `@import "./layered_ui";` line from `application.css`. Your `layered_ui_overrides.css` and any customisations are unaffected.

## [0.17.0] - 2026-05-19

### Added
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ bin/rails generate layered:ui:install
```

The install generator will:
- Copy `layered_ui.css` to `app/assets/tailwind/`
- Add `@import "./layered_ui";` to your `application.css`
- Add `@import "../builds/tailwind/layered_ui";` to your `application.css` (the engine's CSS is served straight from the gem via [tailwindcss-rails' engine support](https://github.com/rails/tailwindcss-rails#rails-engines-support-experimental), so it stays in sync when you upgrade)
- Create `app/assets/tailwind/layered_ui_overrides.css` for your theme customisations (never overwritten on re-install)
- Add `import "layered_ui"` to your `application.js`

Then update your application layout to render the engine layout. Place any `content_for` blocks **above** the render call - the engine layout reads them when it renders, so they must be defined first:
Expand All @@ -101,7 +101,7 @@ All colors are CSS custom properties on `:root` using a two-tier system:

```css
/* app/assets/tailwind/application.css */
@import "./layered_ui";
@import "../builds/tailwind/layered_ui";

:root {
--accent: oklch(0.58 0.19 255);
Expand Down
6 changes: 6 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,10 @@ Rake::TestTask.new(:test) do |t|
t.verbose = false
end

# The engine layout links the compiled Tailwind build (stylesheet_link_tag
# "tailwind"), so the dummy app's CSS must be built before integration tests
# run - otherwise propshaft raises "asset 'tailwind.css' was not found". This
# also generates the engine entry point under app/assets/builds/tailwind/.
task test: "app:tailwindcss:build"

task default: :test
2 changes: 1 addition & 1 deletion app/views/layouts/layered_ui/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

<%# Apply dark class before paint to prevent white flash on reload %>
<script>try{var s=localStorage.getItem("theme");(s==="dark"||(!s&&matchMedia("(prefers-color-scheme:dark)").matches))&&document.documentElement.classList.add("dark")}catch(e){}</script>
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
<%= yield(:l_ui_head) %>
<%= javascript_importmap_tags %>
</head>
Expand Down
8 changes: 5 additions & 3 deletions layered-ui-rails.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@ Gem::Specification.new do |spec|
bin/rails generate layered:ui:install

This command will:
• Copy the layered UI CSS to your host app at app/assets/tailwind/layered_ui.css
• This approach ensures the CSS is processed with your host app's Tailwind configuration
• Add an import statement to your app/assets/tailwind/application.css
• Add `@import "../builds/tailwind/layered_ui";` to your app/assets/tailwind/application.css
• The engine's CSS is served directly from the gem via tailwindcss-rails' engine
support, so it is compiled with your host app's Tailwind configuration and stays
in sync automatically when you upgrade
• Create app/assets/tailwind/layered_ui_overrides.css for your theme customisations
• Add `import "layered_ui"` to your app/javascript/application.js (just after `import "@hotwired/turbo-rails"`, if present)

If these imports already exist, they will not be duplicated.
Expand Down
30 changes: 0 additions & 30 deletions lib/generators/layered/ui/copy_assets_generator.rb

This file was deleted.

2 changes: 1 addition & 1 deletion lib/generators/layered/ui/import_css_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def add_css_import
return unless File.exist?(application_css)

content = File.read(application_css)
import_line = '@import "./layered_ui";'
import_line = '@import "../builds/tailwind/layered_ui";'
overrides_line = '@import "./layered_ui_overrides";'

unless content.include?(import_line)
Expand Down
4 changes: 0 additions & 4 deletions lib/generators/layered/ui/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ def check_dependencies
end
end

def copy_assets
invoke "layered:ui:copy_assets"
end

def create_overrides
invoke "layered:ui:create_overrides"
end
Expand Down
2 changes: 1 addition & 1 deletion test/dummy/app/assets/tailwind/application.css
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
@import "tailwindcss";
@import "../../../../../app/assets/tailwind/layered/ui/styles.css";
@import "../builds/tailwind/layered_ui";
6 changes: 3 additions & 3 deletions test/dummy/app/views/pages/home.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ curl -fsSL https://raw.githubusercontent.com/layered-ai-public/layered-ui-rails/
<pre class="l-ui-surface mt-4"><code>bundle install</code></pre>

<h2 class="mt-6">Setup</h2>
<p class="mt-4">Run the install generator to copy CSS and JS to your application:</p>
<p class="mt-4">Run the install generator to wire up CSS and JS in your application:</p>
<pre class="l-ui-surface mt-4"><code>bin/rails generate layered:ui:install</code></pre>
<p class="mt-4">This will:</p>
<ul class="l-ui-list mt-2">
<li>Copy <code>layered_ui.css</code> to <code>app/assets/tailwind/</code></li>
<li>Add <code>@import "./layered_ui";</code> to your application.css</li>
<li>Add <code>@import "../builds/tailwind/layered_ui";</code> to your application.css (the engine's CSS is served straight from the gem via tailwindcss-rails' engine support, so it stays in sync when you upgrade)</li>
<li>Create <code>layered_ui_overrides.css</code> for your theme customisations</li>
<li>Add <code>import "layered_ui"</code> to your application.js</li>
</ul>

Expand Down
25 changes: 0 additions & 25 deletions test/generators/copy_assets_generator_test.rb

This file was deleted.

6 changes: 3 additions & 3 deletions test/generators/import_css_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@ class ImportCssGeneratorTest < Rails::Generators::TestCase

assert_file "app/assets/tailwind/application.css" do |content|
assert_match '@import "tailwindcss";', content
assert_match '@import "./layered_ui";', content
assert_match '@import "../builds/tailwind/layered_ui";', content
end
end

test "does not duplicate existing import" do
FileUtils.mkdir_p File.join(destination_root, "app/assets/tailwind")
File.write File.join(destination_root, "app/assets/tailwind/application.css"),
%(@import "tailwindcss";\n@import "./layered_ui";)
%(@import "tailwindcss";\n@import "../builds/tailwind/layered_ui";)

Dir.chdir(destination_root) { run_generator }

assert_file "app/assets/tailwind/application.css" do |content|
assert_equal 1, content.scan('@import "./layered_ui";').count
assert_equal 1, content.scan('@import "../builds/tailwind/layered_ui";').count
end
end
end
3 changes: 1 addition & 2 deletions test/generators/install_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ class InstallGeneratorTest < Rails::Generators::TestCase

Dir.chdir(destination_root) { run_generator }

assert_file "app/assets/tailwind/layered_ui.css"
assert_file "app/assets/tailwind/application.css", /@import "\.\/layered_ui"/
assert_file "app/assets/tailwind/application.css", %r{@import "\.\./builds/tailwind/layered_ui"}
assert_file "app/javascript/application.js", /import "layered_ui"/
end
end
Loading