Tailwind CSS
Hozo’s primary styling frontend is Tailwind CSS v4. You write standard utility classes on Hozo primitives (View, Text, Pressable, Heading, and the rest), and the Rust compiler lowers them at build time into scoped CSS rules on the Web and StyleSheet objects on iOS and Android.
Reading your @theme through Tailwind’s own resolver
Section titled “Reading your @theme through Tailwind’s own resolver”Tailwind v4 defines design tokens in CSS — @theme { --color-brand: oklch(62% 0.19 258); } — alongside @import, @plugin, and cascade layers. Parsing that file with regexes or a hand-rolled CSS reader would disagree with Tailwind as soon as an @import chain or colour function was involved.
Instead, @hozo/tailwind runs your entry stylesheet through Tailwind’s own resolver once per build and hands the resolved theme table to the compiler:
/* src/theme.css */@import "tailwindcss";@import "@hozo/ui/theme.css";@theme { --color-brand: oklch(62% 0.19 258); --radius-control: 10px;}// vite.config.tsimport { hozo } from '@hozo/vite'import { defineConfig } from 'vite'export default defineConfig({ plugins: [hozo({ css: 'src/theme.css' })],})Three details of that resolution matter across platforms:
- Colours come back in three forms: the token name, the authored
oklch()value, and a precomputed hex fallback. Web CSS emits the modern colour or CSS variable reference; React Native has nooklch()parser, so the Native backend receives the converted sRGB/hex value automatically. - Spacing stays a scale step, not frozen pixels:
p-4means four units of--spacing. Keeping the step until lowering means a project that changes--spacingmoves its spacing scale as a unit rather than baking0.25reminto the theme loader. - Radius tokens resolve at compile time on both platforms: overriding
--radius-lg: 10pxor defining--radius-control: 10pxmakesrounded-lg,rounded-t-lg, androunded-controlemit10pxon Web andborderRadius: 10on React Native. Barerounded,rounded-none,rounded-full, and arbitrary values such asrounded-[8px]keep their explicit meanings.
Variants across Web and React Native
Section titled “Variants across Web and React Native”On the Web, variants compile to real CSS selectors and media queries (:hover, :focus-visible, @media (min-width: 768px), @media (prefers-color-scheme: dark)).
On React Native, there is no selector engine. Hozo bridges variants through two mechanisms:
- Interaction states (
hover:,active:,focus:,focus-visible:,disabled:): lowered into state callbacks on interactive primitives (Pressable,Button,Link,TextInput) and into registered style-slot callbacks on@hozo/patternsand@hozo/formwidgets. - Environment conditions (
sm:,md:,lg:,xl:,2xl:,dark:,motion-safe:,motion-reduce:,portrait:,landscape:): lowered to subscribe to@hozo/engine’s shared runtime environment hooks (useHozoEnv) only when a component actually uses one of those variants. A component with no responsive or dark classes subscribes to nothing.
Layout features React Native does not have natively
Section titled “Layout features React Native does not have natively”Several CSS layout features have no direct React Native StyleSheet property. Rather than dropping them silently, the Native backend lowers supported static subsets to small generated runtime wrappers in @hozo/primitives/generated/*:
- CSS Grid (
grid,grid-cols-*,col-span-*,gap-*): lowered toHozoGridandHozoGridItemon React Native for staticfr,px, andminmax(px, fr)tracks. - Container queries (
@container,@md:...): lowered toHozoContainer, measuring the container’s layout width viaonLayout. - Child spacing (
space-x-*,space-y-*,divide-*): lowered to child-spacing helpers when static children require separator margins or borders. - Transitions (
transition-*,duration-*,ease-*): lowered toAnimatedinterpolation on React Native for numeric transform and opacity properties. - Relative typography (
text-sm,text-lg,em,Heading,Small): propagated through Hozo’s inherited text frame so nestedTextnodes scale relative to their parent as they do in CSS.
When React Native cannot draw a class
Section titled “When React Native cannot draw a class”When a utility uses a CSS capability that React Native has no equivalent for — such as before:block, backdrop:bg-black/50, float-left, columns-2, or scroll-snap-type — Hozo does not pretend it worked:
- In the compiler: the Native backend emits
WEB_ONLY_PROPERTY_ON_NATIVE(orUNSUPPORTED_CLASS_ON_NATIVE), naming the class and the reason. - In
/docs/components/: every@hozo/uicomponent page runsnativeClassReportover the component’s source and prints the exact classes React Native cannot draw under the preview.
Programmatic theme loading
Section titled “Programmatic theme loading”Build integrations call @hozo/tailwind automatically. Tools that inspect a project outside a bundler can call it directly:
import { loadProjectTheme, loadStaticProjectTheme } from '@hozo/tailwind'// Full Tailwind v4 resolver (used by Vite, Metro, Next, and Storybook integrations)const theme = await loadProjectTheme(projectRoot, { css: 'src/theme.css', warn: (message) => console.warn(message),})// Read-only structural analysis with content hashes (used by @hozo/migration-audit)const staticTheme = await loadStaticProjectTheme(projectRoot, 'src/theme.css')loadStaticProjectTheme parses entry and imported CSS structurally without executing @plugin, @config, or remote stylesheets, recording content hashes so migration audits can verify whether two runs shared the exact same theme inputs.
See it live
Section titled “See it live”- How styles are resolved: the three compilation tiers (static, structurally conditional
cx/cn, and the candidate stylesheet fallback). - Tailwind, compiled: interactive side-by-side view of one component’s TSX, generated Web CSS, and React Native
StyleSheetoutput. - Conformance Matrix: live differential count of every Tailwind v4 utility tested against Tailwind’s own engine and React Native’s schema.