Phoenix LiveView 1.2 Delivers Colocated CSS and Compiler Overhaul
The latest release introduces scoped component styling and a refactored HEEx engine to streamline asset management.
The Phoenix Framework team has officially released LiveView 1.2.0, introducing a major step forward in how Elixir developers manage component-specific assets. Building on the colocated JavaScript features introduced in version 1.1, LiveView 1.2 brings colocated CSS directly into HEEx templates.
To support these new asset-extraction pipelines without overcomplicating the core compilation process, the release also features a significant under-the-hood overhaul of the HEEx compiler engine. Developers looking to upgrade can adopt the new version by updating their dependency requirement in mix.exs:
{:phoenix_live_view, "~> 1.2.0"}
Colocated CSS and Asset Extraction
In LiveView 1.1, developers gained the ability to write colocated JavaScript hooks directly inside HEEx templates. LiveView 1.2 extends this paradigm to stylesheets. By using a special :type attribute on a <style> tag, developers can define styles inline alongside their component markup:
def table(assigns) do
~H"""
<style :type={MyApp.ColocatedCSS}>
thead {
color: #111827;
}
tbody, tr:hover {
background-color: #f9fafb;
}
</style>
<table>...</table>
"""
end
At compile time, the :type attribute instructs the HEEx compiler to extract the contents of the <style> tag into a dedicated phoenix-colocated folder inside the project's _build directory. From there, standard asset bundlers like Esbuild or Tailwind CSS can pick up and process the extracted styles as part of the application's normal build pipeline.
Component Scoping and the @scope Rule
One of the primary challenges of colocating CSS is preventing styles from leaking and accidentally affecting other elements on the page. To address this, LiveView 1.2 leverages the modern CSS @scope rule, which restricts style rules to a specific DOM subtree.
To make scoping work, the compiler must precisely identify the boundaries of a template. This becomes complex when templates render slots, as slot contents belong to the caller's scope rather than the component's scope. LiveView solves this by annotating the rendered HTML with boundary markers.
When configured, LiveView automatically injects a unique phx-css-* attribute to the root of the styled component, and a phx-r attribute to all outermost "root" elements of any template (including slots). For example, a styled component rendering a slot will produce HTML structured like this:
<p phx-r>Hello World</p>
<div phx-r phx-css-foo>
<p>Component Content</p>
<ul>
<li>
<p phx-r>Slot Content (Caller Scope)</p>
</li>
</ul>
</div>
This structure allows LiveView to generate a scoped CSS rule targeting only the elements belonging to the component itself:
@scope ([phx-css-foo]) to ([phx-r]) {
p {
font-weight: bold;
}
}
Under this rule, only the <p> tags belonging directly to the component's template are styled, while the outer <p> and the slot's nested <p> remain untouched.
Because browser support for the native CSS @scope rule is not yet universal, LiveView 1.2 does not enable automatic scoping by default. Instead, the framework provides a @behaviour that developers can implement to define custom scoping strategies. The native @scope implementation is provided in the documentation for early adopters. To opt into boundary tracking, developers must configure the root tag attribute in their configuration:
config :phoenix_live_view,
root_tag_attribute: "phx-r"
Rebuilding the HEEx Compiler
Implementing colocated CSS required the Phoenix team to fundamentally change how HEEx templates are compiled. Previously, template compilation and formatting shared duplicated code paths, which made adding macro-like behaviors (such as CSS and JS extraction) difficult to maintain.
To resolve this, the HEEx compilation process has been split into distinct tokenization and parsing steps. This architectural split allows LiveView to handle macro components cleanly during parsing without increasing the complexity of the rest of the compilation pipeline. It also allows the compiler and the HTML formatter to share the same underlying parsing logic.
Developer Experience and Tooling Enhancements
Beyond colocated CSS, LiveView 1.2 introduces several quality-of-life improvements for Elixir developers:
- Custom Tag Formatters: Developers can now implement the
Phoenix.LiveView.HTMLFormatter.TagFormatterbehaviour to format<script>and<style>tags within HEEx templates using external tools like Prettier. - Automatic JS Struct Encoding:
Phoenix.LiveView.JSstructs are now automatically serialized when sent viapush_eventif the application uses Jason or the built-in JSON module. Developers can also manually serialize them usingJS.to_encodable/1. - Granular Debug Annotations: HEEx debug annotations can now be toggled on a per-module basis using the
@debug_heex_annotationsand@debug_attributesmodule attributes. - Categorized Test Warnings: Compiler and test warnings can now be configured and filtered by specific categories to reduce test suite noise.
Sources & further reading
- Phoenix LiveView 1.2 — phoenixframework.org
Ji-ho covers the increasingly tangled overlap between cloud architecture and security, drawing on a background as a penetration tester to keep his reporting grounded in real-world attack paths. He never lets a vendor claim go unquestioned and insists that every buzzword come with a proof of concept.
Discussion 0
No comments yet
Be the first to weigh in.