useDocumentClass
Adds classes and attributes to the page <html> and <body> while the calling component is mounted, merged with everyone else.
import { useDocumentClass } from '@salla.sa/twilight-theme-engine/hooks/useDocumentClass';In plain words
A page component normally draws inside the page, not on <body> or <html>. With useDocumentClass({ body: { class: 'page-cart' } }), the body gets that class while your component is on screen, and loses it when the component is removed.
Several components can do this at once. Their classes are combined; for any other attribute, the most recent one wins.
Signature
function useDocumentClass(descriptor: DocumentElementDescriptor): void
// DocumentElementDescriptor and DocumentElementAttrs are exported from
// @salla.sa/twilight-theme-engine/utils
interface DocumentElementDescriptor {
body?: DocumentElementAttrs;
html?: DocumentElementAttrs;
}
interface DocumentElementAttrs {
class?: string | string[]; // merged across components
[attr: string]: string | string[] | undefined; // most recent wins
}Try it live
import { useDocumentClass } from '@salla.sa/twilight-theme-engine/hooks/useDocumentClass';
export function CompactLayout({ children }: { children: React.ReactNode }) {
useDocumentClass({
body: { class: 'playground-compact', 'data-playground-density': 'compact' },
});
return <>{children}</>;
}
Example
import { useDocumentClass } from '@salla.sa/twilight-theme-engine/hooks/useDocumentClass';
export function CheckoutShell({ children }: { children: React.ReactNode }) {
useDocumentClass({
body: { class: ['page-checkout', 'no-sticky-header'], 'data-flow': 'checkout' },
});
return <main className="checkout-shell">{children}</main>;
}
How it behaves
Under
TwilightProvider(which rendersDocumentClassProvider) each call registers into one store, keyed byuseId(), and a single sync component writes the merged result todocument.bodyanddocument.documentElement.classvalues are union-merged and de-duplicated. Any other attribute takes the value of the most recent registration; a registration whose content changes is removed and added again, so it becomes the most recent.The engine itself registers
html: { lang, dir }and the theme's body classes (color-mode-…,footer-is-dark…) this way, fromThemeDocumentSync.It re-registers only when
JSON.stringify(descriptor)changes, so an inline object literal is fine.Without any provider it writes to the DOM directly and records what it set in a
data-de-managedattribute.
Gotchas
It registers in an effect, so the attributes are not in the server HTML. CSS that depends on them (above-the-fold layout, hiding a header) flashes after hydration. Put attributes that must be there from the first paint on
<html>/<body>in your root route instead.Setting
langordircompetes with the engine's own registration of those attributes; whichever registered last wins, and a later change of language re-registers the engine's.docs/20-document-class.md lists
DocumentElementDescriptoron the package root. Import it from@salla.sa/twilight-theme-engine/utils.