<style href precedence> element rendered next to the markup, and React 19 hoists those into <head> and dedupes them by href. Styling becomes rendering, which is why it also streams: nothing waits for a commit that Suspense may split.react-server export condition resolves to a build of Box that calls no hook, schedules no effect and never touches the DOM. Importing the package from a Server Component is the whole setup.href is a hash of the rule text, and class names are content hashes too — so the class the server resolved is the class the browser bundle resolves, and two Boxes with p={4} produce one style element.// app/page.tsx — a Server Component.
import Flex from '@cronocode/react-box/components/flex';
import { H1, P } from '@cronocode/react-box/components/semantics';
export default function Page() {
return (
<Flex d="column" gap={2} p={6} bgColor="slate-50" borderRadius={2} theme={{ dark: { bgColor: 'slate-900' } }}>
<H1 fontSize={24} fontWeight={600}>Rendered on the server</H1>
<P color="slate-600" md={{ fontSize: 16 }} hover={{ color: 'sky-500' }}>
Pseudo-classes, breakpoints and themes all work — they are just more rules.
</P>
</Flex>
);
}// app/elementMode.ts — imported by every 'use client' module in the app.
'use client';
import Box from '@cronocode/react-box';
// The client bundle resolves the client Box, which inserts rules through the CSSOM after
// hydration — so an island's CSS would be missing from the server-rendered HTML. This puts
// it back in the markup, on the same emission path the server used.
Box.configure({ sink: 'element' });Box.Theme needs state, storage and a media-query listener, so it stays client-only — but theme rules are ancestor-scoped (.dark .someClass), which means a server component only has to put the theme name on <html>. Switching it later is a DOM write, and createThemeController() is that state machine with no React in it.// app/layout.tsx — a Server Component.
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className="dark" data-theme="dark">
<body>{children}</body>
</html>
);
}// app/themeToggle.tsx — the island that changes it.
'use client';
import { createThemeController } from '@cronocode/react-box/core';
const controller = createThemeController({ storageKey: 'theme', theme: 'dark' });
export default function ThemeToggle() {
return <button onClick={() => controller.set(controller.theme === 'dark' ? 'light' : 'dark')}>Theme</button>;
}p losing against px. Every rule is therefore wrapped in a cascade layer, and the base element declares the full layer order once, up front. Layer order beats source order, so this mode is exactly as deterministic as the CSSOM one. Two consequences are worth knowing:<style> element in the other modes.{({ isHover }) => …}) — they need state. The server Box throws a message saying so.Box.Theme, for the same reason. Theme styles are unaffected.Dropdown, Tooltip, DataGrid, Checkbox, Select, Form. Their chunks ship a 'use client' banner, so a Server Component can still import one — it just becomes a client boundary. The hook-free ones (Flex, Grid, Button, Textbox, H1, …) render on the server like Box does.examples/next-app in the repository is a Next.js App Router app whose pages are Server Components, with a streamed Suspense boundary, a client island, the theme toggle above and a page of pre-built components rendered by the server. Its smoke test starts the production server and asserts on the HTML that the base element was hoisted, that every class in the markup has its rule in the response, and that the streamed section brought its own CSS with it.npm run build # the library itself
npm run build:next-app # packs dist/, installs it, builds the app
npm run smoke:next-app # 13 assertions against the served HTML