template-kit/no-inline-style
The template-kit/no-inline-style authoring rule — what it enforces, why, and how to fix it.
No inline
style=in server-rendered section JSX. Islands are exempt — but an island is not exempt fromno-hex.
Whether a file is server-rendered or an island is decided per file, by the "use client"
directive — see server vs client. On an island this rule installs
no visitors at all.
In a server-rendered file it reports any JSX attribute named style, on any element.
The value is never inspected: a static object, a computed one, a spread, a string — all
the same. There is no “but this one is fine” case in a server-rendered file.
The island exemption does not extend to colour. no-hex reads inside style values
and has no island exemption at all, so this still fails on a "use client" file:
"use client";
export default function Badge() { return <div style={{ color: "#ff0000" }} />; // template-kit/no-hex}Exempt from this rule; still a hard-coded colour.
Reason
Section titled “Reason”Three concrete failures, all of them server-render concerns:
- It bypasses the cascade. An inline style is the highest-specificity declaration
short of
!important, so a template’s theme can no longer restyle the section. The section stops being portable. - It skips Tailwind’s responsive prefixes.
style={{ padding: 16 }}has no breakpoint form; there is no way to say “16 on mobile, 48 on desktop” in it. Utilities have exactly that (p-4 lg:p-12), which is why the layout ladder lives in classes. - It churns per-call identity that the editor’s hash-based diff cannot dedupe — the editor re-renders the section on every keystroke, and a fresh style object each render is work it cannot skip.
An island computing a transform from live pointer state has none of these problems: it runs in the browser, it is not part of the cascade the theme is styling, and that is precisely what an island is for. So it is exempt.
Use Tailwind utility classes. For a value that only exists at runtime, move the markup
into a "use client" island, where style is allowed.
Before
Section titled “Before”import { Section } from "@homepages/template-kit";
import type { Props } from "./schema.js";
export function Renderer({ slots }: Props) { return ( <Section> <div style={{ padding: 16, display: "flex", gap: 8 }}> <h2>{slots.headline}</h2> </div> </Section> );}import { Section } from "@homepages/template-kit";
import type { Props } from "./schema.js";
export function Renderer({ slots }: Props) { return ( <Section> <div className="flex gap-2 p-4"> <h2>{slots.headline}</h2> </div> </Section> );}The runtime-value case — a transform driven by live state — has no utility form, and is the case the island exemption exists for:
"use client";
import { useState } from "react";import type { ReactNode } from "react";
export default function Parallax({ children }: { children: ReactNode }) { const [offset, setOffset] = useState(0); return ( <div onPointerMove={(e) => setOffset(e.clientX / 40)} style={{ transform: `translateX(${offset}px)` }} > {children} </div> );}See also
Section titled “See also”- Server vs client — the island exemption, and the one rule that does not take it.
- Theme and CSS — the token utilities and the breakpoint ladder.