Skip to main content

Use content_for :l_ui_head to inject content into the layout <head>, after the stylesheet. This is the right place for dynamic <style> blocks, preload hints, or other per-request head content.

Dynamic theme tokens

Override CSS custom properties per request (e.g. per-tenant branding) by injecting a <style> block. Placing it after the stylesheet ensures the cascade override takes effect correctly.

<% content_for :l_ui_head do %>
  <style>
    :root { --accent: <%= @tenant.accent_color %>; --accent-foreground: oklch(1 0 0); }
  </style>
<% end %>

Security: never interpolate user-supplied strings directly into a <style> tag - this allows CSS injection. Validate or sanitise any user-derived values before interpolation.

See the colors page for the full list of design tokens.

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 using Rails' content_security_policy_nonce helper - Rails includes the matching nonce in the CSP header automatically:

<% content_for :l_ui_head do %>
  <style nonce="<%= content_security_policy_nonce %>">
    :root { --accent: <%= @tenant.accent_color %>; --accent-foreground: oklch(1 0 0); }
  </style>
<% end %>

Turbo Drive compatibility

Turbo Drive merges <head> content on each navigation - changed <style> blocks are replaced correctly. The caveat is the preview pass: when Turbo restores a snapshot from its cache before the network response arrives, a cached snapshot may briefly show stale tokens.

If tokens are stable for a session (e.g. a single-tenant session where branding only changes on login), add data-turbo-track="reload" to the <style> tag. Turbo will trigger a full page reload only when the tag content changes between visits.

If tokens vary per navigation (e.g. a user switches between tenants mid-session), data-turbo-track="reload" will reload on every visit, effectively disabling Turbo. The preferred pattern in that case is to serve the theme as a dedicated stylesheet URL:

<% content_for :l_ui_head do %>
  <link rel="stylesheet" href="<%= @tenant.theme_stylesheet_url %>">
<% end %>

Turbo caches and reuses stylesheet responses by URL, so this approach is both Turbo-friendly and avoids inline style blocks entirely.